[alsa-devel] [RFC PATCH 00/40] soundwire: updates for 5.4
The existing upstream code allows for SoundWire devices to be enumerated and managed by the bus, but streaming is not currently supported.
Bard Liao, Rander Wang and I did quite a bit of integration/validation work to close this gap and we now have SoundWire streaming + basic power managemement on Intel CometLake and IceLake reference boards. These changes are still preliminary and should not be merged as is, but it's time to start reviews. While the number of patches is quite large, each of the changes is quite small.
SOF driver changes will be submitted shortly as well but are still being validated.
ClockStop modes and synchronized playback on multiple links are not supported for now and will likely be part of the next cycle (dependencies on codec drivers and multi-cpu DAI support).
Acknowledgements: This work would not have been possible without the support of Slawomir Blauciak and Tomasz Lauda on the SOF side, currently being reviewed, see https://github.com/thesofproject/sof/pull/1638
Comments and feedback welcome!
Bard liao (1): soundwire: include mod_devicetable.h to avoid compiling warnings
Pierre-Louis Bossart (38): soundwire: add debugfs support soundwire: cadence_master: add debugfs register dump soundwire: cadence_master: align debugfs to 8 digits soundwire: intel: add debugfs register dump soundwire: intel: move interrupt enable after interrupt handler registration soundwire: intel: prevent possible dereference in hw_params soundwire: intel: fix channel number reported by hardware soundwire: intel: remove BIOS work-arounds soundwire: cadence_master: fix usage of CONFIG_UPDATE soundwire: cadence_master: remove useless wrapper soundwire: cadence_master: simplify bus clash interrupt clear soundwire: cadence_master: revisit interrupt settings soundwire: cadence_master: fix register definition for SLAVE_STATE soundwire: cadence_master: fix definitions for INTSTAT0/1 soundwire: cadence_master: handle multiple status reports per Slave soundwire: cadence_master: improve startup sequence with link hw_reset soundwire: bus: use runtime_pm_get_sync/pm when enabled soundwire: bus: split handling of Device0 events soundwire: bus: improve dynamic debug comments for enumeration soundwire: prototypes for suspend/resume soundwire: export helpers to find row and column values soundwire: stream: fix disable sequence soundwire: cadence_master: use BIOS defaults for frame shape soundwire: intel: use BIOS information to set clock dividers soundwire: Add Intel resource management algorithm soundwire: intel: handle disabled links soundwire: intel_init: add kernel module parameter to filter out links soundwire: cadence_master: add kernel parameter to override interrupt mask soundwire: intel: move shutdown() callback and don't export symbol soundwire: intel: add helper for initialization soundwire: intel: Add basic power management support soundwire: intel: ignore disabled links for suspend/resume soundwire: intel: export helper to exit reset soundwire: intel: disable interrupts on suspend soundwire: cadence_master: add hw_reset capability in debugfs soundwire: cadence_master: make clock stop exit configurable on init soundwire: intel: add pm_runtime support soundwire: intel: add delay on restart for enumeration
Rander Wang (1): soundwire: cadence_master: fix divider setting in clock register
drivers/soundwire/Makefile | 4 +- drivers/soundwire/algo_dynamic_allocation.c | 403 ++++++++++++++++++++ drivers/soundwire/bus.c | 44 ++- drivers/soundwire/bus.h | 77 +++- drivers/soundwire/bus_type.c | 3 + drivers/soundwire/cadence_master.c | 365 ++++++++++++++---- drivers/soundwire/cadence_master.h | 12 +- drivers/soundwire/debugfs.c | 156 ++++++++ drivers/soundwire/intel.c | 381 +++++++++++++++++- drivers/soundwire/intel_init.c | 14 + drivers/soundwire/slave.c | 1 + drivers/soundwire/stream.c | 53 ++- include/linux/soundwire/sdw.h | 15 + 13 files changed, 1414 insertions(+), 114 deletions(-) create mode 100644 drivers/soundwire/algo_dynamic_allocation.c create mode 100644 drivers/soundwire/debugfs.c
Add base debugfs mechanism for SoundWire bus by creating soundwire root and master-N and slave-x hierarchy.
Also add SDW Slave SCP, DP0 and DP-N register debug file.
Registers not implemented will print as "XX"
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/Makefile | 4 +- drivers/soundwire/bus.c | 6 ++ drivers/soundwire/bus.h | 24 ++++++ drivers/soundwire/bus_type.c | 3 + drivers/soundwire/debugfs.c | 156 ++++++++++++++++++++++++++++++++++ drivers/soundwire/slave.c | 1 + include/linux/soundwire/sdw.h | 4 + 7 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 drivers/soundwire/debugfs.c
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index fd99a831b92a..88990cac48a7 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,7 +4,9 @@ #
#Bus Objs -soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o +soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o \ + debugfs.o + obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
#Cadence Objs diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fe745830a261..5ad4109dc72f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -49,6 +49,8 @@ int sdw_add_bus_master(struct sdw_bus *bus) } }
+ bus->debugfs = sdw_bus_debugfs_init(bus); + /* * Device numbers in SoundWire are 0 through 15. Enumeration device * number (0), Broadcast device number (15), Group numbers (12 and @@ -109,6 +111,8 @@ static int sdw_delete_slave(struct device *dev, void *data) struct sdw_slave *slave = dev_to_sdw_dev(dev); struct sdw_bus *bus = slave->bus;
+ sdw_slave_debugfs_exit(slave->debugfs); + mutex_lock(&bus->bus_lock);
if (slave->dev_num) /* clear dev_num if assigned */ @@ -130,6 +134,8 @@ static int sdw_delete_slave(struct device *dev, void *data) void sdw_delete_bus_master(struct sdw_bus *bus) { device_for_each_child(bus->dev, NULL, sdw_delete_slave); + + sdw_bus_debugfs_exit(bus->debugfs); } EXPORT_SYMBOL(sdw_delete_bus_master);
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 3048ca153f22..06ac4adb0074 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -18,6 +18,30 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus); +void sdw_bus_debugfs_exit(struct dentry *d); +struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave); +void sdw_slave_debugfs_exit(struct dentry *d); +void sdw_debugfs_init(void); +void sdw_debugfs_exit(void); +#else +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{ return NULL; } + +void sdw_bus_debugfs_exit(struct dentry *d) {} + +struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{ return NULL; } + +void sdw_slave_debugfs_exit(struct dentry *d) {} + +void sdw_debugfs_init(void) {} + +void sdw_debugfs_exit(void) {} + +#endif + enum { SDW_MSG_FLAG_READ = 0, SDW_MSG_FLAG_WRITE, diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2655602f0cfb..4a465f55039f 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -6,6 +6,7 @@ #include <linux/pm_domain.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> +#include "bus.h"
/** * sdw_get_device_id - find the matching SoundWire device id @@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver);
static int __init sdw_bus_init(void) { + sdw_debugfs_init(); return bus_register(&sdw_bus_type); }
static void __exit sdw_bus_exit(void) { + sdw_debugfs_exit(); bus_unregister(&sdw_bus_type); }
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c new file mode 100644 index 000000000000..8d86e100516e --- /dev/null +++ b/drivers/soundwire/debugfs.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2017-19 Intel Corporation. + +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include "bus.h" + +#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_debugfs_root; +#endif + +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{ + struct dentry *d; + char name[16]; + + if (!sdw_debugfs_root) + return NULL; + + /* create the debugfs master-N */ + snprintf(name, sizeof(name), "master-%d", bus->link_id); + d = debugfs_create_dir(name, sdw_debugfs_root); + + return d; +} + +void sdw_bus_debugfs_exit(struct dentry *d) +{ + debugfs_remove_recursive(d); +} + +#define RD_BUF (3 * PAGE_SIZE) + +static ssize_t sdw_sprintf(struct sdw_slave *slave, + char *buf, size_t pos, unsigned int reg) +{ + int value; + + value = sdw_read(slave, reg); + + if (value < 0) + return scnprintf(buf + pos, RD_BUF - pos, "%3x\tXX\n", reg); + else + return scnprintf(buf + pos, RD_BUF - pos, + "%3x\t%2x\n", reg, value); +} + +static ssize_t sdw_slave_reg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sdw_slave *slave = file->private_data; + unsigned int reg; + char *buf; + ssize_t ret; + int i, j; + + buf = kzalloc(RD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = scnprintf(buf, RD_BUF, "Register Value\n"); + ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n"); + + for (i = 0; i < 6; i++) + ret += sdw_sprintf(slave, buf, ret, i); + + ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n"); + ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN); + for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++) + ret += sdw_sprintf(slave, buf, ret, i); + + ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n"); + ret += sdw_sprintf(slave, buf, ret, + SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET); + for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET; + i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++) + ret += sdw_sprintf(slave, buf, ret, i); + + ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n"); + for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++) + ret += sdw_sprintf(slave, buf, ret, i); + for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++) + ret += sdw_sprintf(slave, buf, ret, i); + + ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n"); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B0); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B0); + + ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n"); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B1); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B1); + + for (i = 1; i < 14; i++) { + ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i); + reg = SDW_DPN_INT(i); + for (j = 0; j < 6; j++) + ret += sdw_sprintf(slave, buf, ret, reg + j); + + ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n"); + reg = SDW_DPN_CHANNELEN_B0(i); + for (j = 0; j < 9; j++) + ret += sdw_sprintf(slave, buf, ret, reg + j); + + ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n"); + reg = SDW_DPN_CHANNELEN_B1(i); + for (j = 0; j < 9; j++) + ret += sdw_sprintf(slave, buf, ret, reg + j); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + + return ret; +} + +static const struct file_operations sdw_slave_reg_fops = { + .open = simple_open, + .read = sdw_slave_reg_read, + .llseek = default_llseek, +}; + +struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{ + struct dentry *master; + struct dentry *d; + char name[32]; + + master = slave->bus->debugfs; + + /* create the debugfs slave-name */ + snprintf(name, sizeof(name), "%s", dev_name(&slave->dev)); + d = debugfs_create_dir(name, master); + + debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops); + + return d; +} + +void sdw_slave_debugfs_exit(struct dentry *d) +{ + debugfs_remove_recursive(d); +} + +void sdw_debugfs_init(void) +{ + sdw_debugfs_root = debugfs_create_dir("soundwire", NULL); +} + +void sdw_debugfs_exit(void) +{ + debugfs_remove_recursive(sdw_debugfs_root); +} diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index f39a5815e25d..34d8bb995f45 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -56,6 +56,7 @@ static int sdw_slave_add(struct sdw_bus *bus, mutex_unlock(&bus->bus_lock); put_device(&slave->dev); } + slave->debugfs = sdw_slave_debugfs_init(slave);
return ret; } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 3b231472464a..a49028e9d666 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -544,6 +544,7 @@ struct sdw_slave_ops { * @bus: Bus handle * @ops: Slave callback ops * @prop: Slave properties + * @debugfs: Slave debugfs * @node: node for bus list * @port_ready: Port ready completion flag for each Slave port * @dev_num: Device Number assigned by Bus @@ -555,6 +556,7 @@ struct sdw_slave { struct sdw_bus *bus; const struct sdw_slave_ops *ops; struct sdw_slave_prop prop; + struct dentry *debugfs; struct list_head node; struct completion *port_ready; u16 dev_num; @@ -731,6 +733,7 @@ struct sdw_master_ops { * @m_rt_list: List of Master instance of all stream(s) running on Bus. This * is used to compute and program bus bandwidth, clock, frame shape, * transport and port parameters + * @debugfs: Bus debugfs * @defer_msg: Defer message * @clk_stop_timeout: Clock stop timeout computed * @bank_switch_timeout: Bank switch timeout computed @@ -750,6 +753,7 @@ struct sdw_bus { struct sdw_bus_params params; struct sdw_master_prop prop; struct list_head m_rt_list; + struct dentry *debugfs; struct sdw_defer defer_msg; unsigned int clk_stop_timeout; u32 bank_switch_timeout;
Hi Pierre,
A couple of nitpicks:
On Thu, Jul 25, 2019 at 06:39:53PM -0500, Pierre-Louis Bossart wrote:
Add base debugfs mechanism for SoundWire bus by creating soundwire root and master-N and slave-x hierarchy.
Also add SDW Slave SCP, DP0 and DP-N register debug file.
Registers not implemented will print as "XX"
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/Makefile | 4 +- drivers/soundwire/bus.c | 6 ++ drivers/soundwire/bus.h | 24 ++++++ drivers/soundwire/bus_type.c | 3 + drivers/soundwire/debugfs.c | 156 ++++++++++++++++++++++++++++++++++ drivers/soundwire/slave.c | 1 + include/linux/soundwire/sdw.h | 4 + 7 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 drivers/soundwire/debugfs.c
[snip]
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 3048ca153f22..06ac4adb0074 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -18,6 +18,30 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus); +void sdw_bus_debugfs_exit(struct dentry *d); +struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave); +void sdw_slave_debugfs_exit(struct dentry *d); +void sdw_debugfs_init(void); +void sdw_debugfs_exit(void); +#else +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{ return NULL; }
static?
+void sdw_bus_debugfs_exit(struct dentry *d) {}
+struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{ return NULL; }
+void sdw_slave_debugfs_exit(struct dentry *d) {}
+void sdw_debugfs_init(void) {}
+void sdw_debugfs_exit(void) {}
Same for all the above. You could also declare them inline, but I really hope the compiler will be smart enough to do that itself.
+#endif
enum { SDW_MSG_FLAG_READ = 0, SDW_MSG_FLAG_WRITE, diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2655602f0cfb..4a465f55039f 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -6,6 +6,7 @@ #include <linux/pm_domain.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> +#include "bus.h"
/**
- sdw_get_device_id - find the matching SoundWire device id
@@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver);
static int __init sdw_bus_init(void) {
- sdw_debugfs_init(); return bus_register(&sdw_bus_type);
}
static void __exit sdw_bus_exit(void) {
- sdw_debugfs_exit(); bus_unregister(&sdw_bus_type);
}
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c new file mode 100644 index 000000000000..8d86e100516e --- /dev/null +++ b/drivers/soundwire/debugfs.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2017-19 Intel Corporation.
+#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include "bus.h"
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_debugfs_root; +#endif
+struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{
- struct dentry *d;
I would remove the above
- char name[16];
- if (!sdw_debugfs_root)
return NULL;
- /* create the debugfs master-N */
- snprintf(name, sizeof(name), "master-%d", bus->link_id);
- d = debugfs_create_dir(name, sdw_debugfs_root);
- return d;
And just do
+ return debugfs_create_dir(name, sdw_debugfs_root);
+}
+void sdw_bus_debugfs_exit(struct dentry *d) +{
- debugfs_remove_recursive(d);
+}
+#define RD_BUF (3 * PAGE_SIZE)
+static ssize_t sdw_sprintf(struct sdw_slave *slave,
char *buf, size_t pos, unsigned int reg)
+{
- int value;
- value = sdw_read(slave, reg);
I personally would join the two lines above, but that's just a personal preference.
- if (value < 0)
return scnprintf(buf + pos, RD_BUF - pos, "%3x\tXX\n", reg);
- else
I think it's advised to not use an else in such cases.
Thanks Guennadi
return scnprintf(buf + pos, RD_BUF - pos,
"%3x\t%2x\n", reg, value);
+}
+static ssize_t sdw_slave_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_slave *slave = file->private_data;
- unsigned int reg;
- char *buf;
- ssize_t ret;
- int i, j;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n");
- for (i = 0; i < 6; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
- ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN);
- for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
- ret += sdw_sprintf(slave, buf, ret,
SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET);
- for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET;
i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n");
- for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B0);
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B0);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B1);
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B1);
- for (i = 1; i < 14; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i);
reg = SDW_DPN_INT(i);
for (j = 0; j < 6; j++)
ret += sdw_sprintf(slave, buf, ret, reg + j);
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
reg = SDW_DPN_CHANNELEN_B0(i);
for (j = 0; j < 9; j++)
ret += sdw_sprintf(slave, buf, ret, reg + j);
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
reg = SDW_DPN_CHANNELEN_B1(i);
for (j = 0; j < 9; j++)
ret += sdw_sprintf(slave, buf, ret, reg + j);
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+}
+static const struct file_operations sdw_slave_reg_fops = {
- .open = simple_open,
- .read = sdw_slave_reg_read,
- .llseek = default_llseek,
+};
+struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{
- struct dentry *master;
- struct dentry *d;
- char name[32];
- master = slave->bus->debugfs;
- /* create the debugfs slave-name */
- snprintf(name, sizeof(name), "%s", dev_name(&slave->dev));
- d = debugfs_create_dir(name, master);
- debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);
- return d;
+}
+void sdw_slave_debugfs_exit(struct dentry *d) +{
- debugfs_remove_recursive(d);
+}
+void sdw_debugfs_init(void) +{
- sdw_debugfs_root = debugfs_create_dir("soundwire", NULL);
+}
+void sdw_debugfs_exit(void) +{
- debugfs_remove_recursive(sdw_debugfs_root);
+} diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index f39a5815e25d..34d8bb995f45 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -56,6 +56,7 @@ static int sdw_slave_add(struct sdw_bus *bus, mutex_unlock(&bus->bus_lock); put_device(&slave->dev); }
slave->debugfs = sdw_slave_debugfs_init(slave);
return ret;
} diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 3b231472464a..a49028e9d666 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -544,6 +544,7 @@ struct sdw_slave_ops {
- @bus: Bus handle
- @ops: Slave callback ops
- @prop: Slave properties
- @debugfs: Slave debugfs
- @node: node for bus list
- @port_ready: Port ready completion flag for each Slave port
- @dev_num: Device Number assigned by Bus
@@ -555,6 +556,7 @@ struct sdw_slave { struct sdw_bus *bus; const struct sdw_slave_ops *ops; struct sdw_slave_prop prop;
- struct dentry *debugfs; struct list_head node; struct completion *port_ready; u16 dev_num;
@@ -731,6 +733,7 @@ struct sdw_master_ops {
- @m_rt_list: List of Master instance of all stream(s) running on Bus. This
- is used to compute and program bus bandwidth, clock, frame shape,
- transport and port parameters
- @debugfs: Bus debugfs
- @defer_msg: Defer message
- @clk_stop_timeout: Clock stop timeout computed
- @bank_switch_timeout: Bank switch timeout computed
@@ -750,6 +753,7 @@ struct sdw_bus { struct sdw_bus_params params; struct sdw_master_prop prop; struct list_head m_rt_list;
- struct dentry *debugfs; struct sdw_defer defer_msg; unsigned int clk_stop_timeout; u32 bank_switch_timeout;
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On 7/25/19 5:15 PM, Guennadi Liakhovetski wrote:
Hi Pierre,
A couple of nitpicks:
Thanks for the feedback!
create mode 100644 drivers/soundwire/debugfs.c
[snip]
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 3048ca153f22..06ac4adb0074 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -18,6 +18,30 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus); +void sdw_bus_debugfs_exit(struct dentry *d); +struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave); +void sdw_slave_debugfs_exit(struct dentry *d); +void sdw_debugfs_init(void); +void sdw_debugfs_exit(void); +#else +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{ return NULL; }
static?
+void sdw_bus_debugfs_exit(struct dentry *d) {}
+struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{ return NULL; }
+void sdw_slave_debugfs_exit(struct dentry *d) {}
+void sdw_debugfs_init(void) {}
+void sdw_debugfs_exit(void) {}
Same for all the above. You could also declare them inline, but I really hope the compiler will be smart enough to do that itself.
yes, I'll add static inline for all this.
+struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{
- struct dentry *d;
I would remove the above
- char name[16];
- if (!sdw_debugfs_root)
return NULL;
- /* create the debugfs master-N */
- snprintf(name, sizeof(name), "master-%d", bus->link_id);
- d = debugfs_create_dir(name, sdw_debugfs_root);
- return d;
And just do
- return debugfs_create_dir(name, sdw_debugfs_root);
yep, will do.
+static ssize_t sdw_sprintf(struct sdw_slave *slave,
char *buf, size_t pos, unsigned int reg)
+{
- int value;
- value = sdw_read(slave, reg);
I personally would join the two lines above, but that's just a personal preference.
I prefer splitting variables and code, I just can't mentally split the two.
- if (value < 0)
return scnprintf(buf + pos, RD_BUF - pos, "%3x\tXX\n", reg);
- else
I think it's advised to not use an else in such cases.
Thanks Guennadi
return scnprintf(buf + pos, RD_BUF - pos,
"%3x\t%2x\n", reg, value);
+}
The intent was to provide a visual cue that the register is not implemented, which is quite useful. Not all registers are mandatory and not all vendors document the entire set of registers, so it's a good way to figure things out. The value is not used for any functional purpose, it's just a register dump for the integrator to look at. I'll add a note to explain the idea.
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
+static ssize_t sdw_slave_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_slave *slave = file->private_data;
- unsigned int reg;
- char *buf;
- ssize_t ret;
- int i, j;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n");
- for (i = 0; i < 6; i++)
ret += sdw_sprintf(slave, buf, ret, i);
In most cases explicit reg macro is used, here it's implicit. Align with the rest?
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
- ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN);
- for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
- ret += sdw_sprintf(slave, buf, ret,
SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET);
- for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET;
i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++)
ret += sdw_sprintf(slave, buf, ret, i);
I'd advice to revisit macros declarations first. There should be SDW_DP0_SAMPLECTRL1_B(bank) declared. In general all macros for SDW should be "bank-less" (name wise). Additionally, SDW_BANK_OFFSET(bank) could be provided for convenience i.e.: return 0 for bank0. Yeah, there might be some speed loss in terms of operation count but in most cases it is negligible.
Would simplify this entire reg dump greatly. const array on top with {0, 1} elements and replacing explicit "bank0/1" strings with "bank%d" gets code size reduced while not losing on readability.
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n");
- for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++)
ret += sdw_sprintf(slave, buf, ret, i);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B0);
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B0);
- ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B1);
- ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B1);
- for (i = 1; i < 14; i++) {
Explicit valid slave addresses would be preferred.
ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i);
reg = SDW_DPN_INT(i);
for (j = 0; j < 6; j++)
ret += sdw_sprintf(slave, buf, ret, reg + j);
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
reg = SDW_DPN_CHANNELEN_B0(i);
for (j = 0; j < 9; j++)
ret += sdw_sprintf(slave, buf, ret, reg + j);
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
reg = SDW_DPN_CHANNELEN_B1(i);
for (j = 0; j < 9; j++)
ret += sdw_sprintf(slave, buf, ret, reg + j);
Some sort of MAX_CHANNELS would be nice here too.
- }
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+}
+static const struct file_operations sdw_slave_reg_fops = {
- .open = simple_open,
- .read = sdw_slave_reg_read,
- .llseek = default_llseek,
+};
+struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{
- struct dentry *master;
- struct dentry *d;
- char name[32];
- master = slave->bus->debugfs;
- /* create the debugfs slave-name */
- snprintf(name, sizeof(name), "%s", dev_name(&slave->dev));
- d = debugfs_create_dir(name, master);
- debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);
Pointer returned by _create_file gets completely ignored here. At least dbg msg would be nice if it fails.
- return d;
+}
Thanks for the feedback Cezary.
+static ssize_t sdw_slave_reg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sdw_slave *slave = file->private_data; + unsigned int reg; + char *buf; + ssize_t ret; + int i, j;
+ buf = kzalloc(RD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM;
+ ret = scnprintf(buf, RD_BUF, "Register Value\n"); + ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n");
+ for (i = 0; i < 6; i++) + ret += sdw_sprintf(slave, buf, ret, i);
In most cases explicit reg macro is used, here it's implicit. Align with the rest?
I don't see what you are referring to, or I need more coffee. we use this function sdw_printf in a number of places. Or are you referring to the magic value 6? That should indeed be a macro.
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n"); + ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN); + for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++) + ret += sdw_sprintf(slave, buf, ret, i);
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n"); + ret += sdw_sprintf(slave, buf, ret, + SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET); + for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET; + i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++) + ret += sdw_sprintf(slave, buf, ret, i);
I'd advice to revisit macros declarations first. There should be SDW_DP0_SAMPLECTRL1_B(bank) declared. In general all macros for SDW should be "bank-less" (name wise). Additionally, SDW_BANK_OFFSET(bank) could be provided for convenience i.e.: return 0 for bank0. Yeah, there might be some speed loss in terms of operation count but in most cases it is negligible.
Would simplify this entire reg dump greatly. const array on top with {0, 1} elements and replacing explicit "bank0/1" strings with "bank%d" gets code size reduced while not losing on readability.
This could require a lot of changes in other parts of the code, and I don't want to do this just for debugfs. It's valid point that maybe the code can be simplified, but the changes are an across-the-board change to be done when we don't add new functionality. I'll keep this on the todo list.
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n"); + for (i = SDW_SCP_INT1; i <= SDW_SCP_BANKDELAY; i++) + ret += sdw_sprintf(slave, buf, ret, i); + for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++) + ret += sdw_sprintf(slave, buf, ret, i);
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n"); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B0); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B0);
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n"); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_FRAMECTRL_B1); + ret += sdw_sprintf(slave, buf, ret, SDW_SCP_NEXTFRAME_B1);
+ for (i = 1; i < 14; i++) {
Explicit valid slave addresses would be preferred.
no, these are ports. we should use a macro instead of the magic 14 but it's fine to try and read all ports. As I explained it's a good way to figure out how many ports the Slave device supports even in the absence of documentation. This also helps figure out if the DisCo properties make sense.
+ ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i); + reg = SDW_DPN_INT(i); + for (j = 0; j < 6; j++) + ret += sdw_sprintf(slave, buf, ret, reg + j);
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n"); + reg = SDW_DPN_CHANNELEN_B0(i); + for (j = 0; j < 9; j++) + ret += sdw_sprintf(slave, buf, ret, reg + j);
+ ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n"); + reg = SDW_DPN_CHANNELEN_B1(i); + for (j = 0; j < 9; j++) + ret += sdw_sprintf(slave, buf, ret, reg + j);
Some sort of MAX_CHANNELS would be nice here too.
Yes, need to use macros indeed.
+struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{ + struct dentry *master; + struct dentry *d; + char name[32];
+ master = slave->bus->debugfs;
+ /* create the debugfs slave-name */ + snprintf(name, sizeof(name), "%s", dev_name(&slave->dev)); + d = debugfs_create_dir(name, master);
+ debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);
Pointer returned by _create_file gets completely ignored here. At least dbg msg would be nice if it fails.
+ return d;
I understood that Greg KH doesn't want us to depend on the result of debugfs calls, but a dev_dbg is likely ok.
On Thu, Jul 25, 2019 at 06:39:53PM -0500, Pierre-Louis Bossart wrote:
Add base debugfs mechanism for SoundWire bus by creating soundwire root and master-N and slave-x hierarchy.
Also add SDW Slave SCP, DP0 and DP-N register debug file.
Registers not implemented will print as "XX"
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/Makefile | 4 +- drivers/soundwire/bus.c | 6 ++ drivers/soundwire/bus.h | 24 ++++++ drivers/soundwire/bus_type.c | 3 + drivers/soundwire/debugfs.c | 156 ++++++++++++++++++++++++++++++++++ drivers/soundwire/slave.c | 1 + include/linux/soundwire/sdw.h | 4 + 7 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 drivers/soundwire/debugfs.c
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index fd99a831b92a..88990cac48a7 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,7 +4,9 @@ #
#Bus Objs -soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o +soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o \
debugfs.o
obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
#Cadence Objs diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fe745830a261..5ad4109dc72f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -49,6 +49,8 @@ int sdw_add_bus_master(struct sdw_bus *bus) } }
- bus->debugfs = sdw_bus_debugfs_init(bus);
- /*
- Device numbers in SoundWire are 0 through 15. Enumeration device
- number (0), Broadcast device number (15), Group numbers (12 and
@@ -109,6 +111,8 @@ static int sdw_delete_slave(struct device *dev, void *data) struct sdw_slave *slave = dev_to_sdw_dev(dev); struct sdw_bus *bus = slave->bus;
sdw_slave_debugfs_exit(slave->debugfs);
mutex_lock(&bus->bus_lock);
if (slave->dev_num) /* clear dev_num if assigned */
@@ -130,6 +134,8 @@ static int sdw_delete_slave(struct device *dev, void *data) void sdw_delete_bus_master(struct sdw_bus *bus) { device_for_each_child(bus->dev, NULL, sdw_delete_slave);
- sdw_bus_debugfs_exit(bus->debugfs);
} EXPORT_SYMBOL(sdw_delete_bus_master);
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 3048ca153f22..06ac4adb0074 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -18,6 +18,30 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus); +void sdw_bus_debugfs_exit(struct dentry *d); +struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave); +void sdw_slave_debugfs_exit(struct dentry *d); +void sdw_debugfs_init(void); +void sdw_debugfs_exit(void); +#else +struct dentry *sdw_bus_debugfs_init(struct sdw_bus *bus) +{ return NULL; }
+void sdw_bus_debugfs_exit(struct dentry *d) {}
+struct dentry *sdw_slave_debugfs_init(struct sdw_slave *slave) +{ return NULL; }
+void sdw_slave_debugfs_exit(struct dentry *d) {}
+void sdw_debugfs_init(void) {}
+void sdw_debugfs_exit(void) {}
+#endif
enum { SDW_MSG_FLAG_READ = 0, SDW_MSG_FLAG_WRITE, diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2655602f0cfb..4a465f55039f 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -6,6 +6,7 @@ #include <linux/pm_domain.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> +#include "bus.h"
/**
- sdw_get_device_id - find the matching SoundWire device id
@@ -177,11 +178,13 @@ EXPORT_SYMBOL_GPL(sdw_unregister_driver);
static int __init sdw_bus_init(void) {
- sdw_debugfs_init(); return bus_register(&sdw_bus_type);
}
static void __exit sdw_bus_exit(void) {
- sdw_debugfs_exit(); bus_unregister(&sdw_bus_type);
}
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c new file mode 100644 index 000000000000..8d86e100516e --- /dev/null +++ b/drivers/soundwire/debugfs.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
No, for debugfs-specific code, that dual license makes no sense, right? Don't cargo-cult SPDX identifiers please.
+// Copyright(c) 2017-19 Intel Corporation.
Spell the year out fully unless you want lawyers knocking on your door :)
+#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include "bus.h"
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_debugfs_root; +#endif
This whole file is not built if that option is not enabled, so why the #ifdef?
thanks,
greg k-h
diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c new file mode 100644 index 000000000000..8d86e100516e --- /dev/null +++ b/drivers/soundwire/debugfs.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
No, for debugfs-specific code, that dual license makes no sense, right? Don't cargo-cult SPDX identifiers please.
It's a miss, I used EXPORT_GPL and missed this line, will fix.
+// Copyright(c) 2017-19 Intel Corporation.
Spell the year out fully unless you want lawyers knocking on your door :)
haha, will fix.
+#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include "bus.h"
+#ifdef CONFIG_DEBUG_FS +struct dentry *sdw_debugfs_root; +#endif
This whole file is not built if that option is not enabled, so why the #ifdef?
Ah, will look into this, thanks!
Add debugfs file to dump the Cadence master registers
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 98 ++++++++++++++++++++++++++++++ drivers/soundwire/cadence_master.h | 2 + 2 files changed, 100 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index ff4badc9b3de..91e8bacb83e3 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -8,6 +8,7 @@
#include <linux/delay.h> #include <linux/device.h> +#include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -223,6 +224,103 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/* + * debugfs + */ + +#define RD_BUF (2 * PAGE_SIZE) + +static ssize_t cdns_sprintf(struct sdw_cdns *cdns, + char *buf, size_t pos, unsigned int reg) +{ + return scnprintf(buf + pos, RD_BUF - pos, + "%4x\t%4x\n", reg, cdns_readl(cdns, reg)); +} + +static ssize_t cdns_reg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sdw_cdns *cdns = file->private_data; + char *buf; + ssize_t ret; + int i, j; + + buf = kzalloc(RD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = scnprintf(buf, RD_BUF, "Register Value\n"); + ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n"); + for (i = 0; i < 8; i++) /* 8 MCP registers */ + ret += cdns_sprintf(cdns, buf, ret, i * 4); + + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nStatus & Intr Registers\n"); + for (i = 0; i < 13; i++) /* 13 Status & Intr registers */ + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_STAT + i * 4); + + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nSSP & Clk ctrl Registers\n"); + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0); + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1); + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0); + ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1); + + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nDPn B0 Registers\n"); + for (i = 0; i < 7; i++) { + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nDP-%d\n", i); + for (j = 0; j < 6; j++) + ret += cdns_sprintf(cdns, buf, ret, + CDNS_DPN_B0_CONFIG(i) + j * 4); + } + + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nDPn B1 Registers\n"); + for (i = 0; i < 7; i++) { + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nDP-%d\n", i); + + for (j = 0; j < 6; j++) + ret += cdns_sprintf(cdns, buf, ret, + CDNS_DPN_B1_CONFIG(i) + j * 4); + } + + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nDPn Control Registers\n"); + for (i = 0; i < 7; i++) + ret += cdns_sprintf(cdns, buf, ret, + CDNS_PORTCTRL + i * CDNS_PORT_OFFSET); + + ret += scnprintf(buf + ret, RD_BUF - ret, + "\nPDIn Config Registers\n"); + for (i = 0; i < 7; i++) + ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + + return ret; +} + +static const struct file_operations cdns_reg_fops = { + .open = simple_open, + .read = cdns_reg_read, + .llseek = default_llseek, +}; + +/** + * sdw_cdns_debugfs_init() - Cadence debugfs init + * @cdns: Cadence instance + * @root: debugfs root + */ +void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) +{ + debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); +} +EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); + /* * IO Calls */ diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index fe2af62958b1..c0bf6ff00a44 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -163,6 +163,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
+void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); + int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir);
On Thu, Jul 25, 2019 at 06:39:54PM -0500, Pierre-Louis Bossart wrote:
Add debugfs file to dump the Cadence master registers
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 98 ++++++++++++++++++++++++++++++ drivers/soundwire/cadence_master.h | 2 + 2 files changed, 100 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index ff4badc9b3de..91e8bacb83e3 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -8,6 +8,7 @@
#include <linux/delay.h> #include <linux/device.h> +#include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -223,6 +224,103 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/*
- debugfs
- */
+#define RD_BUF (2 * PAGE_SIZE)
+static ssize_t cdns_sprintf(struct sdw_cdns *cdns,
char *buf, size_t pos, unsigned int reg)
+{
- return scnprintf(buf + pos, RD_BUF - pos,
"%4x\t%4x\n", reg, cdns_readl(cdns, reg));
+}
+static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_cdns *cdns = file->private_data;
- char *buf;
- ssize_t ret;
- int i, j;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n");
- for (i = 0; i < 8; i++) /* 8 MCP registers */
ret += cdns_sprintf(cdns, buf, ret, i * 4);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nStatus & Intr Registers\n");
- for (i = 0; i < 13; i++) /* 13 Status & Intr registers */
ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_STAT + i * 4);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nSSP & Clk ctrl Registers\n");
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn B0 Registers\n");
- for (i = 0; i < 7; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDP-%d\n", i);
for (j = 0; j < 6; j++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_DPN_B0_CONFIG(i) + j * 4);
- }
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn B1 Registers\n");
- for (i = 0; i < 7; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDP-%d\n", i);
for (j = 0; j < 6; j++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_DPN_B1_CONFIG(i) + j * 4);
- }
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn Control Registers\n");
- for (i = 0; i < 7; i++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_PORTCTRL + i * CDNS_PORT_OFFSET);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nPDIn Config Registers\n");
- for (i = 0; i < 7; i++)
ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i));
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+}
+static const struct file_operations cdns_reg_fops = {
- .open = simple_open,
- .read = cdns_reg_read,
- .llseek = default_llseek,
+};
DEFINE_SHOW_ATTRIBUTE()?
thanks,
greg k-h
+static const struct file_operations cdns_reg_fops = {
- .open = simple_open,
- .read = cdns_reg_read,
- .llseek = default_llseek,
+};
DEFINE_SHOW_ATTRIBUTE()?
I remember looking at this but can't recall why I left it this way. That was before my Summer break so will relook, thanks for suggesting this.
On Thu, Jul 25, 2019 at 06:39:54PM -0500, Pierre-Louis Bossart wrote:
Add debugfs file to dump the Cadence master registers
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 98 ++++++++++++++++++++++++++++++ drivers/soundwire/cadence_master.h | 2 + 2 files changed, 100 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index ff4badc9b3de..91e8bacb83e3 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -8,6 +8,7 @@
#include <linux/delay.h> #include <linux/device.h> +#include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -223,6 +224,103 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/*
- debugfs
- */
+#define RD_BUF (2 * PAGE_SIZE)
+static ssize_t cdns_sprintf(struct sdw_cdns *cdns,
char *buf, size_t pos, unsigned int reg)
+{
- return scnprintf(buf + pos, RD_BUF - pos,
"%4x\t%4x\n", reg, cdns_readl(cdns, reg));
+}
+static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_cdns *cdns = file->private_data;
- char *buf;
- ssize_t ret;
- int i, j;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n");
- for (i = 0; i < 8; i++) /* 8 MCP registers */
ret += cdns_sprintf(cdns, buf, ret, i * 4);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nStatus & Intr Registers\n");
- for (i = 0; i < 13; i++) /* 13 Status & Intr registers */
ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_STAT + i * 4);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nSSP & Clk ctrl Registers\n");
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn B0 Registers\n");
- for (i = 0; i < 7; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDP-%d\n", i);
for (j = 0; j < 6; j++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_DPN_B0_CONFIG(i) + j * 4);
- }
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn B1 Registers\n");
- for (i = 0; i < 7; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDP-%d\n", i);
for (j = 0; j < 6; j++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_DPN_B1_CONFIG(i) + j * 4);
- }
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn Control Registers\n");
- for (i = 0; i < 7; i++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_PORTCTRL + i * CDNS_PORT_OFFSET);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nPDIn Config Registers\n");
- for (i = 0; i < 7; i++)
please use macros for all the hardcodings.
ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i));
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+}
+static const struct file_operations cdns_reg_fops = {
- .open = simple_open,
- .read = cdns_reg_read,
- .llseek = default_llseek,
+};
+/**
- sdw_cdns_debugfs_init() - Cadence debugfs init
- @cdns: Cadence instance
- @root: debugfs root
- */
+void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) +{
- debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
+} +EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
/*
- IO Calls
*/ diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index fe2af62958b1..c0bf6ff00a44 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -163,6 +163,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
+void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir); -- 2.20.1
--
+static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_cdns *cdns = file->private_data;
- char *buf;
- ssize_t ret;
- int i, j;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n");
- for (i = 0; i < 8; i++) /* 8 MCP registers */
ret += cdns_sprintf(cdns, buf, ret, i * 4);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nStatus & Intr Registers\n");
- for (i = 0; i < 13; i++) /* 13 Status & Intr registers */
ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_STAT + i * 4);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nSSP & Clk ctrl Registers\n");
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0);
- ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn B0 Registers\n");
- for (i = 0; i < 7; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDP-%d\n", i);
for (j = 0; j < 6; j++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_DPN_B0_CONFIG(i) + j * 4);
- }
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn B1 Registers\n");
- for (i = 0; i < 7; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDP-%d\n", i);
for (j = 0; j < 6; j++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_DPN_B1_CONFIG(i) + j * 4);
- }
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nDPn Control Registers\n");
- for (i = 0; i < 7; i++)
ret += cdns_sprintf(cdns, buf, ret,
CDNS_PORTCTRL + i * CDNS_PORT_OFFSET);
- ret += scnprintf(buf + ret, RD_BUF - ret,
"\nPDIn Config Registers\n");
- for (i = 0; i < 7; i++)
please use macros for all the hardcodings.
yes, I completely changed that part in the upcoming update by using register start/stop for all loops, it makes the code more consistent and easier to change (SoundWire 1.2 registers will need to be added)
SQUASHME
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 91e8bacb83e3..9f611a1fff0a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -234,7 +234,7 @@ static ssize_t cdns_sprintf(struct sdw_cdns *cdns, char *buf, size_t pos, unsigned int reg) { return scnprintf(buf + pos, RD_BUF - pos, - "%4x\t%4x\n", reg, cdns_readl(cdns, reg)); + "%4x\t%8x\n", reg, cdns_readl(cdns, reg)); }
static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
SQUASHME
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 91e8bacb83e3..9f611a1fff0a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -234,7 +234,7 @@ static ssize_t cdns_sprintf(struct sdw_cdns *cdns, char *buf, size_t pos, unsigned int reg) { return scnprintf(buf + pos, RD_BUF - pos,
"%4x\t%4x\n", reg, cdns_readl(cdns, reg));
"%4x\t%8x\n", reg, cdns_readl(cdns, reg));
}
static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
Should just be merged together with the introducing commit. Guess it's posted unintentionally.
On 7/26/19 4:38 AM, Cezary Rojewski wrote:
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
SQUASHME
Signed-off-by: Pierre-Louis Bossart
pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 91e8bacb83e3..9f611a1fff0a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -234,7 +234,7 @@ static ssize_t cdns_sprintf(struct sdw_cdns *cdns, char *buf, size_t pos, unsigned int reg) { return scnprintf(buf + pos, RD_BUF - pos, - "%4x\t%4x\n", reg, cdns_readl(cdns, reg)); + "%4x\t%8x\n", reg, cdns_readl(cdns, reg)); } static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
Should just be merged together with the introducing commit. Guess it's posted unintentionally.
Yep, I missed this, will squash in the updates as intended.
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
SQUASHME
Git trick!
commit this using: git fixup <sha1>
where sha1 is commit where you want this to be squashed into!
then below will suuash them for you!
git rebase -i --autosquash
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 91e8bacb83e3..9f611a1fff0a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -234,7 +234,7 @@ static ssize_t cdns_sprintf(struct sdw_cdns *cdns, char *buf, size_t pos, unsigned int reg) { return scnprintf(buf + pos, RD_BUF - pos,
"%4x\t%4x\n", reg, cdns_readl(cdns, reg));
"%4x\t%8x\n", reg, cdns_readl(cdns, reg));
}
static ssize_t cdns_reg_read(struct file *file, char __user *user_buf,
2.20.1
Add debugfs file to dump the Intel SoundWire registers
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 317873bc0555..aeadc341c0a3 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -6,6 +6,7 @@ */
#include <linux/acpi.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -16,6 +17,7 @@ #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_intel.h> #include "cadence_master.h" +#include "bus.h" #include "intel.h"
/* Intel SHIM Registers Definition */ @@ -98,6 +100,7 @@ struct sdw_intel { struct sdw_cdns cdns; int instance; struct sdw_intel_link_res *res; + struct dentry *fs; };
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) @@ -161,6 +164,115 @@ static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) return -EAGAIN; }
+/* + * debugfs + */ + +#define RD_BUF (2 * PAGE_SIZE) + +static ssize_t intel_sprintf(void __iomem *mem, bool l, + char *buf, size_t pos, unsigned int reg) +{ + int value; + + if (l) + value = intel_readl(mem, reg); + else + value = intel_readw(mem, reg); + + return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value); +} + +static ssize_t intel_reg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sdw_intel *sdw = file->private_data; + void __iomem *s = sdw->res->shim; + void __iomem *a = sdw->res->alh; + char *buf; + ssize_t ret; + int i, j; + unsigned int links, reg; + + buf = kzalloc(RD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0); + + ret = scnprintf(buf, RD_BUF, "Register Value\n"); + ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); + + for (i = 0; i < 4; i++) { + reg = SDW_SHIM_LCAP + i * 4; + ret += intel_sprintf(s, true, buf, ret, reg); + } + + for (i = 0; i < links; i++) { + ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i)); + + for (j = 0; j < 8; j++) { + ret += intel_sprintf(s, false, buf, ret, + SDW_SHIM_PCMSYCHM(i, j)); + ret += intel_sprintf(s, false, buf, ret, + SDW_SHIM_PCMSYCHC(i, j)); + } + + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i)); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i)); + } + + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN); + ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS); + + ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH\n"); + for (i = 0; i < 8; i++) + ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + + return ret; +} + +static const struct file_operations intel_reg_fops = { + .open = simple_open, + .read = intel_reg_read, + .llseek = default_llseek, +}; + +static void intel_debugfs_init(struct sdw_intel *sdw) +{ + struct dentry *root = sdw->cdns.bus.debugfs; + + if (!root) + return; + + sdw->fs = debugfs_create_dir("intel-sdw", root); + if (IS_ERR_OR_NULL(sdw->fs)) { + dev_err(sdw->cdns.dev, "debugfs root creation failed\n"); + sdw->fs = NULL; + return; + } + + debugfs_create_file("intel-registers", 0400, sdw->fs, sdw, + &intel_reg_fops); + + sdw_cdns_debugfs_init(&sdw->cdns, sdw->fs); +} + +static void intel_debugfs_exit(struct sdw_intel *sdw) +{ + debugfs_remove_recursive(sdw->fs); +} + /* * shim ops */ @@ -896,6 +1008,8 @@ static int intel_probe(struct platform_device *pdev) goto err_dai; }
+ intel_debugfs_init(sdw); + return 0;
err_dai: @@ -912,6 +1026,7 @@ static int intel_remove(struct platform_device *pdev)
sdw = platform_get_drvdata(pdev);
+ intel_debugfs_exit(sdw); free_irq(sdw->res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); sdw_delete_bus_master(&sdw->cdns.bus);
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
+static void intel_debugfs_init(struct sdw_intel *sdw) +{
- struct dentry *root = sdw->cdns.bus.debugfs;
- if (!root)
return;
- sdw->fs = debugfs_create_dir("intel-sdw", root);
- if (IS_ERR_OR_NULL(sdw->fs)) {
dev_err(sdw->cdns.dev, "debugfs root creation failed\n");
sdw->fs = NULL;
return;
- }
- debugfs_create_file("intel-registers", 0400, sdw->fs, sdw,
&intel_reg_fops);
- sdw_cdns_debugfs_init(&sdw->cdns, sdw->fs);
+}
I believe there should be dummy equivalent of _init and _exit if debugfs is not enabled (if these are defined already and I've missed it, please ignore).
+static void intel_debugfs_exit(struct sdw_intel *sdw) +{
- debugfs_remove_recursive(sdw->fs);
+}
On 7/26/19 4:35 AM, Cezary Rojewski wrote:
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
+static void intel_debugfs_init(struct sdw_intel *sdw) +{ + struct dentry *root = sdw->cdns.bus.debugfs;
+ if (!root) + return;
+ sdw->fs = debugfs_create_dir("intel-sdw", root); + if (IS_ERR_OR_NULL(sdw->fs)) { + dev_err(sdw->cdns.dev, "debugfs root creation failed\n"); + sdw->fs = NULL; + return; + }
+ debugfs_create_file("intel-registers", 0400, sdw->fs, sdw, + &intel_reg_fops);
+ sdw_cdns_debugfs_init(&sdw->cdns, sdw->fs); +}
I believe there should be dummy equivalent of _init and _exit if debugfs is not enabled (if these are defined already and I've missed it, please ignore).
I think the direction is just to keep going if there is an error or debufs is not enabled.
+static void intel_debugfs_exit(struct sdw_intel *sdw) +{ + debugfs_remove_recursive(sdw->fs); +}
On Fri, Jul 26, 2019 at 09:00:28AM -0500, Pierre-Louis Bossart wrote:
On 7/26/19 4:35 AM, Cezary Rojewski wrote:
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
+static void intel_debugfs_init(struct sdw_intel *sdw) +{ + struct dentry *root = sdw->cdns.bus.debugfs;
+ if (!root) + return;
+ sdw->fs = debugfs_create_dir("intel-sdw", root); + if (IS_ERR_OR_NULL(sdw->fs)) { + dev_err(sdw->cdns.dev, "debugfs root creation failed\n"); + sdw->fs = NULL; + return; + }
+ debugfs_create_file("intel-registers", 0400, sdw->fs, sdw, + &intel_reg_fops);
+ sdw_cdns_debugfs_init(&sdw->cdns, sdw->fs); +}
I believe there should be dummy equivalent of _init and _exit if debugfs is not enabled (if these are defined already and I've missed it, please ignore).
I think the direction is just to keep going if there is an error or debufs is not enabled.
You should not care either way :)
On Thu, Jul 25, 2019 at 06:39:56PM -0500, Pierre-Louis Bossart wrote:
Add debugfs file to dump the Intel SoundWire registers
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 317873bc0555..aeadc341c0a3 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -6,6 +6,7 @@ */
#include <linux/acpi.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -16,6 +17,7 @@ #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_intel.h> #include "cadence_master.h" +#include "bus.h" #include "intel.h"
/* Intel SHIM Registers Definition */ @@ -98,6 +100,7 @@ struct sdw_intel { struct sdw_cdns cdns; int instance; struct sdw_intel_link_res *res;
- struct dentry *fs;
};
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) @@ -161,6 +164,115 @@ static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) return -EAGAIN; }
+/*
- debugfs
- */
+#define RD_BUF (2 * PAGE_SIZE)
+static ssize_t intel_sprintf(void __iomem *mem, bool l,
char *buf, size_t pos, unsigned int reg)
+{
- int value;
- if (l)
value = intel_readl(mem, reg);
- else
value = intel_readw(mem, reg);
- return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
+}
+static ssize_t intel_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_intel *sdw = file->private_data;
- void __iomem *s = sdw->res->shim;
- void __iomem *a = sdw->res->alh;
- char *buf;
- ssize_t ret;
- int i, j;
- unsigned int links, reg;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
- for (i = 0; i < 4; i++) {
reg = SDW_SHIM_LCAP + i * 4;
ret += intel_sprintf(s, true, buf, ret, reg);
- }
- for (i = 0; i < links; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
for (j = 0; j < 8; j++) {
ret += intel_sprintf(s, false, buf, ret,
SDW_SHIM_PCMSYCHM(i, j));
ret += intel_sprintf(s, false, buf, ret,
SDW_SHIM_PCMSYCHC(i, j));
}
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
- }
- ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
- ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH\n");
- for (i = 0; i < 8; i++)
ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+}
+static const struct file_operations intel_reg_fops = {
- .open = simple_open,
- .read = intel_reg_read,
- .llseek = default_llseek,
+};
DEFINE_SIMPLE_ATTRIBUTE()?
+static void intel_debugfs_init(struct sdw_intel *sdw) +{
- struct dentry *root = sdw->cdns.bus.debugfs;
- if (!root)
return;
- sdw->fs = debugfs_create_dir("intel-sdw", root);
- if (IS_ERR_OR_NULL(sdw->fs)) {
dev_err(sdw->cdns.dev, "debugfs root creation failed\n");
No, come on, don't do that. I've been sweeping the kernel tree to remove this anti-pattern.
The debugfs core will print an error if you got something wrong, just call the function and move on, you NEVER need to check the return value of a debugfs call.
thanks,
greg k-h
On Fri, Jul 26, 2019 at 04:06:35PM +0200, Greg KH wrote:
On Thu, Jul 25, 2019 at 06:39:56PM -0500, Pierre-Louis Bossart wrote:
Add debugfs file to dump the Intel SoundWire registers
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. The main change is the use of scnprintf to avoid known issues with snprintf.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 317873bc0555..aeadc341c0a3 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -6,6 +6,7 @@ */
#include <linux/acpi.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -16,6 +17,7 @@ #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_intel.h> #include "cadence_master.h" +#include "bus.h" #include "intel.h"
/* Intel SHIM Registers Definition */ @@ -98,6 +100,7 @@ struct sdw_intel { struct sdw_cdns cdns; int instance; struct sdw_intel_link_res *res;
- struct dentry *fs;
};
#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) @@ -161,6 +164,115 @@ static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) return -EAGAIN; }
+/*
- debugfs
- */
+#define RD_BUF (2 * PAGE_SIZE)
+static ssize_t intel_sprintf(void __iomem *mem, bool l,
char *buf, size_t pos, unsigned int reg)
+{
- int value;
- if (l)
value = intel_readl(mem, reg);
- else
value = intel_readw(mem, reg);
- return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);
+}
+static ssize_t intel_reg_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
+{
- struct sdw_intel *sdw = file->private_data;
- void __iomem *s = sdw->res->shim;
- void __iomem *a = sdw->res->alh;
- char *buf;
- ssize_t ret;
- int i, j;
- unsigned int links, reg;
- buf = kzalloc(RD_BUF, GFP_KERNEL);
- if (!buf)
return -ENOMEM;
- links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0);
- ret = scnprintf(buf, RD_BUF, "Register Value\n");
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n");
- for (i = 0; i < 4; i++) {
reg = SDW_SHIM_LCAP + i * 4;
ret += intel_sprintf(s, true, buf, ret, reg);
- }
- for (i = 0; i < links; i++) {
ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i));
for (j = 0; j < 8; j++) {
ret += intel_sprintf(s, false, buf, ret,
SDW_SHIM_PCMSYCHM(i, j));
ret += intel_sprintf(s, false, buf, ret,
SDW_SHIM_PCMSYCHC(i, j));
}
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));
ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));
- }
- ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);
- ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS);
- ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH\n");
- for (i = 0; i < 8; i++)
ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i));
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
- return ret;
+}
+static const struct file_operations intel_reg_fops = {
- .open = simple_open,
- .read = intel_reg_read,
- .llseek = default_llseek,
+};
DEFINE_SIMPLE_ATTRIBUTE()?
Oops, I mean DEFINE_SHOW_ATTRIBUTE()?
+static const struct file_operations intel_reg_fops = {
- .open = simple_open,
- .read = intel_reg_read,
- .llseek = default_llseek,
+};
DEFINE_SIMPLE_ATTRIBUTE()?
yes
+static void intel_debugfs_init(struct sdw_intel *sdw) +{
- struct dentry *root = sdw->cdns.bus.debugfs;
- if (!root)
return;
- sdw->fs = debugfs_create_dir("intel-sdw", root);
- if (IS_ERR_OR_NULL(sdw->fs)) {
dev_err(sdw->cdns.dev, "debugfs root creation failed\n");
No, come on, don't do that. I've been sweeping the kernel tree to remove this anti-pattern.
The debugfs core will print an error if you got something wrong, just call the function and move on, you NEVER need to check the return value of a debugfs call.
Yes, sorry to make your blood pressure go up... I missed this one in the cleanups yesterday. will fix.
Not sure why the existing code would enable interrupts without the ability to deal with them.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index aeadc341c0a3..68832e613b1e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -981,8 +981,6 @@ static int intel_probe(struct platform_device *pdev) if (ret) goto err_init;
- ret = sdw_cdns_enable_interrupt(&sdw->cdns); - /* Read the PDI config and initialize cadence PDI */ intel_pdi_init(sdw, &config); ret = sdw_cdns_pdi_init(&sdw->cdns, config); @@ -1000,6 +998,8 @@ static int intel_probe(struct platform_device *pdev) goto err_init; }
+ ret = sdw_cdns_enable_interrupt(&sdw->cdns); + /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) {
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
Not sure why the existing code would enable interrupts without the ability to deal with them.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index aeadc341c0a3..68832e613b1e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -981,8 +981,6 @@ static int intel_probe(struct platform_device *pdev) if (ret) goto err_init;
- ret = sdw_cdns_enable_interrupt(&sdw->cdns);
- /* Read the PDI config and initialize cadence PDI */ intel_pdi_init(sdw, &config); ret = sdw_cdns_pdi_init(&sdw->cdns, config);
@@ -1000,6 +998,8 @@ static int intel_probe(struct platform_device *pdev) goto err_init; }
- ret = sdw_cdns_enable_interrupt(&sdw->cdns);
we should also handle the return
On 8/2/19 6:53 AM, Vinod Koul wrote:
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
Not sure why the existing code would enable interrupts without the ability to deal with them.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index aeadc341c0a3..68832e613b1e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -981,8 +981,6 @@ static int intel_probe(struct platform_device *pdev) if (ret) goto err_init;
- ret = sdw_cdns_enable_interrupt(&sdw->cdns);
- /* Read the PDI config and initialize cadence PDI */ intel_pdi_init(sdw, &config); ret = sdw_cdns_pdi_init(&sdw->cdns, config);
@@ -1000,6 +998,8 @@ static int intel_probe(struct platform_device *pdev) goto err_init; }
- ret = sdw_cdns_enable_interrupt(&sdw->cdns);
we should also handle the return
yes, fixed already
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 68832e613b1e..497879dd9c0d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -509,7 +509,7 @@ static int intel_config_stream(struct sdw_intel *sdw, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id) { - if (sdw->res->ops && sdw->res->ops->config_stream) + if (sdw->res->ops && sdw->res->ops->config_stream && sdw->res->arg) return sdw->res->ops->config_stream(sdw->res->arg, substream, dai, hw_params, link_id);
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 68832e613b1e..497879dd9c0d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -509,7 +509,7 @@ static int intel_config_stream(struct sdw_intel *sdw, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id) {
- if (sdw->res->ops && sdw->res->ops->config_stream)
- if (sdw->res->ops && sdw->res->ops->config_stream && sdw->res->arg) return sdw->res->ops->config_stream(sdw->res->arg, substream, dai, hw_params, link_id);
Hmm, declaring local for sdw->res should prove useful here after addition of 4th sdw->res dereference.
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 68832e613b1e..497879dd9c0d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -509,7 +509,7 @@ static int intel_config_stream(struct sdw_intel *sdw, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id) { - if (sdw->res->ops && sdw->res->ops->config_stream) + if (sdw->res->ops && sdw->res->ops->config_stream && sdw->res->arg) return sdw->res->ops->config_stream(sdw->res->arg, substream, dai, hw_params, link_id);
Hmm, declaring local for sdw->res should prove useful here after addition of 4th sdw->res dereference.
yes, it's an eyesore. I added this to quickly fix a kernel oops while debugging, will simplify. thanks for the note.
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
so you are saying callback arg is mandatory, if so please document that assumption
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 68832e613b1e..497879dd9c0d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -509,7 +509,7 @@ static int intel_config_stream(struct sdw_intel *sdw, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id) {
- if (sdw->res->ops && sdw->res->ops->config_stream)
- if (sdw->res->ops && sdw->res->ops->config_stream && sdw->res->arg) return sdw->res->ops->config_stream(sdw->res->arg, substream, dai, hw_params, link_id);
-- 2.20.1
On 8/2/19 6:55 AM, Vinod Koul wrote:
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
so you are saying callback arg is mandatory, if so please document that assumption
no, what this says is that if a config_stream is provided then it needs to have a valid argument.
I am not sure what you mean by "document that assumption", comment in the code (where?) or SoundWire documentation?
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 68832e613b1e..497879dd9c0d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -509,7 +509,7 @@ static int intel_config_stream(struct sdw_intel *sdw, struct snd_soc_dai *dai, struct snd_pcm_hw_params *hw_params, int link_id) {
- if (sdw->res->ops && sdw->res->ops->config_stream)
- if (sdw->res->ops && sdw->res->ops->config_stream && sdw->res->arg) return sdw->res->ops->config_stream(sdw->res->arg, substream, dai, hw_params, link_id);
-- 2.20.1
On 02-08-19, 10:16, Pierre-Louis Bossart wrote:
On 8/2/19 6:55 AM, Vinod Koul wrote:
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
so you are saying callback arg is mandatory, if so please document that assumption
no, what this says is that if a config_stream is provided then it needs to have a valid argument.
well typically args are not mandatory..
I am not sure what you mean by "document that assumption", comment in the code (where?) or SoundWire documentation?
The callback documentation which in this is in include/linux/soundwire/sdw_intel.h
On 8/2/19 10:57 AM, Vinod Koul wrote:
On 02-08-19, 10:16, Pierre-Louis Bossart wrote:
On 8/2/19 6:55 AM, Vinod Koul wrote:
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
so you are saying callback arg is mandatory, if so please document that assumption
no, what this says is that if a config_stream is provided then it needs to have a valid argument.
well typically args are not mandatory..
I am not sure what you mean by "document that assumption", comment in the code (where?) or SoundWire documentation?
The callback documentation which in this is in include/linux/soundwire/sdw_intel.h
/** * struct sdw_intel_ops: Intel audio driver callback ops * * @config_stream: configure the stream with the hw_params */ struct sdw_intel_ops { int (*config_stream)(void *arg, void *substream, void *dai, void *hw_params, int stream_num); };
all parameters are mandatory really, not sure what you are trying to get at.
On 02-08-19, 11:52, Pierre-Louis Bossart wrote:
On 8/2/19 10:57 AM, Vinod Koul wrote:
On 02-08-19, 10:16, Pierre-Louis Bossart wrote:
On 8/2/19 6:55 AM, Vinod Koul wrote:
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
This should not happen in production systems but we should test for all callback arguments before invoking the config_stream callback.
so you are saying callback arg is mandatory, if so please document that assumption
no, what this says is that if a config_stream is provided then it needs to have a valid argument.
well typically args are not mandatory..
I am not sure what you mean by "document that assumption", comment in the code (where?) or SoundWire documentation?
The callback documentation which in this is in include/linux/soundwire/sdw_intel.h
/**
- struct sdw_intel_ops: Intel audio driver callback ops
- @config_stream: configure the stream with the hw_params
*/ struct sdw_intel_ops { int (*config_stream)(void *arg, void *substream, void *dai, void *hw_params, int stream_num); };
all parameters are mandatory really, not sure what you are trying to get at.
It would be good to make a note that argument is mandatory!
Thanks
PDI2 reports an invalid count, force the correct hardware-supported value
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 497879dd9c0d..51990b192dc0 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -401,6 +401,15 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
if (pcm) { count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); + + /* + * TODO: pdi number 2 reports channel count as 1 even though + * it supports 8 channel. Performing hardcoding for pdi + * number 2. + */ + if (pdi_num == 2) + count = 7; + } else { count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
PDI2 reports an invalid count, force the correct hardware-supported value
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 497879dd9c0d..51990b192dc0 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -401,6 +401,15 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
if (pcm) { count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
/*
* TODO: pdi number 2 reports channel count as 1 even though
* it supports 8 channel. Performing hardcoding for pdi
* number 2.
*/
if (pdi_num == 2)
count = 7;
Is that true for all Intel controllers or some generations. Would it not be better to put this under some flag which is set on platform basis?
- } else { count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
-- 2.20.1
On 8/2/19 6:57 AM, Vinod Koul wrote:
On 25-07-19, 18:39, Pierre-Louis Bossart wrote:
PDI2 reports an invalid count, force the correct hardware-supported value
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 497879dd9c0d..51990b192dc0 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -401,6 +401,15 @@ intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm)
if (pcm) { count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num));
/*
* TODO: pdi number 2 reports channel count as 1 even though
* it supports 8 channel. Performing hardcoding for pdi
* number 2.
*/
if (pdi_num == 2)
count = 7;
Is that true for all Intel controllers or some generations. Would it not be better to put this under some flag which is set on platform basis?
This is true of all controllers released so far. We will change this if the hardware changes.
- } else { count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); count = ((count & SDW_SHIM_PDMSCAP_CPSS) >>
-- 2.20.1
the values passed by all existing BIOS are fine, let's use them as is. The existing code must have been needed only on early prototypes.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 11 ----------- 1 file changed, 11 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 51990b192dc0..c718c9c67a37 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -922,17 +922,6 @@ static int intel_prop_read(struct sdw_bus *bus) /* Initialize with default handler to read all DisCo properties */ sdw_master_read_prop(bus);
- /* BIOS is not giving some values correctly. So, lets override them */ - bus->prop.num_clk_freq = 1; - bus->prop.clk_freq = devm_kcalloc(bus->dev, bus->prop.num_clk_freq, - sizeof(*bus->prop.clk_freq), - GFP_KERNEL); - if (!bus->prop.clk_freq) - return -ENOMEM; - - bus->prop.clk_freq[0] = bus->prop.max_clk_freq; - bus->prop.err_threshold = 5; - return 0; }
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
the values passed by all existing BIOS are fine, let's use them as is. The existing code must have been needed only on early prototypes.
Thanks for this, I am applying this.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 11 ----------- 1 file changed, 11 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 51990b192dc0..c718c9c67a37 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -922,17 +922,6 @@ static int intel_prop_read(struct sdw_bus *bus) /* Initialize with default handler to read all DisCo properties */ sdw_master_read_prop(bus);
- /* BIOS is not giving some values correctly. So, lets override them */
- bus->prop.num_clk_freq = 1;
- bus->prop.clk_freq = devm_kcalloc(bus->dev, bus->prop.num_clk_freq,
sizeof(*bus->prop.clk_freq),
GFP_KERNEL);
- if (!bus->prop.clk_freq)
return -ENOMEM;
- bus->prop.clk_freq[0] = bus->prop.max_clk_freq;
- bus->prop.err_threshold = 5;
- return 0;
}
-- 2.20.1
Per the hardware documentation, all changes to MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL need to be validated with a self-clearing write to MCP_CONFIG_UPDATE.
For some reason, the existing code only does this write to CONFIG_UPDATE when enabling interrupts. Add a helper and do the update when the CONFIG is changed.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 9f611a1fff0a..eb46cf651d62 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -224,6 +224,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/* + * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL + * need to be confirmed with a write to MCP_CONFIG_UPDATE + */ +static int cdns_update_config(struct sdw_cdns *cdns) +{ + int ret; + + ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, + CDNS_MCP_CONFIG_UPDATE_BIT); + if (ret < 0) + dev_err(cdns->dev, "Config update timedout\n"); + + return ret; +} + /* * debugfs */ @@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) { - int ret; - _cdns_enable_interrupt(cdns); - ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT); - if (ret < 0) - dev_err(cdns->dev, "Config update timedout\n");
- return ret; + return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0; + /* commit changes */ + ret = cdns_update_config(cdns); + + return ret; } EXPORT_SYMBOL(sdw_cdns_init);
On Thu, Jul 25, 2019 at 06:40:01PM -0500, Pierre-Louis Bossart wrote:
Per the hardware documentation, all changes to MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL need to be validated with a self-clearing write to MCP_CONFIG_UPDATE.
For some reason, the existing code only does this write to CONFIG_UPDATE when enabling interrupts. Add a helper and do the update when the CONFIG is changed.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 9f611a1fff0a..eb46cf651d62 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -224,6 +224,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/*
- all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL
- need to be confirmed with a write to MCP_CONFIG_UPDATE
- */
+static int cdns_update_config(struct sdw_cdns *cdns) +{
- int ret;
- ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
- if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
- return ret;
+}
/*
- debugfs
*/ @@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) {
int ret;
_cdns_enable_interrupt(cdns);
ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
return ret;
- return 0;
} EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0;
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
+ return cdns_update_config(cdns);
Thanks Guennadi
} EXPORT_SYMBOL(sdw_cdns_init);
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0;
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
- return cdns_update_config(cdns);
yes, will fix. thanks!
On 7/26/2019 7:40 AM, Pierre-Louis Bossart wrote:
Per the hardware documentation, all changes to MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL need to be validated with a self-clearing write to MCP_CONFIG_UPDATE.
For some reason, the existing code only does this write to CONFIG_UPDATE when enabling interrupts. Add a helper and do the update when the CONFIG is changed.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 9f611a1fff0a..eb46cf651d62 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -224,6 +224,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/*
- all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL
- need to be confirmed with a write to MCP_CONFIG_UPDATE
- */
+static int cdns_update_config(struct sdw_cdns *cdns) +{
- int ret;
- ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
- if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
- return ret;
+}
- /*
*/
- debugfs
@@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) {
int ret;
_cdns_enable_interrupt(cdns);
ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
return ret;
Should we add cdns_update_config() here?
- return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0;
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret; } EXPORT_SYMBOL(sdw_cdns_init);
@@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) { - int ret;
_cdns_enable_interrupt(cdns); - ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT); - if (ret < 0) - dev_err(cdns->dev, "Config update timedout\n"); - return ret;
Should we add cdns_update_config() here?
indeed, this would be a good improvement. The code works because we added the exit_reset() sequence which does call cdns_update_config(), but better make this function self-contained. When we enable the clock-stop mode we will not do this reset sequence.
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
/*
- debugfs
*/ @@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) {
int ret;
_cdns_enable_interrupt(cdns);
ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
return ret;
- return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
Rather than ignoring _cdns_enable_interrupt - despite said func always returning 0 - simply do: return _cnds_enable_interrupt(cdns) and flag caller with inline.
Afterwards, one can think if such encapsulation is even required - remove existing sdw_cdns_enable_interrupt and rename _cnds_enable_interrupt?
On 2019-07-26 11:53, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
/* * debugfs */ @@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) { - int ret;
_cdns_enable_interrupt(cdns); - ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT); - if (ret < 0) - dev_err(cdns->dev, "Config update timedout\n"); - return ret; + return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
Rather than ignoring _cdns_enable_interrupt - despite said func always returning 0 - simply do: return _cnds_enable_interrupt(cdns) and flag caller with inline.
Afterwards, one can think if such encapsulation is even required - remove existing sdw_cdns_enable_interrupt and rename _cnds_enable_interrupt?
Nevermind, I see you simplified it in the next patch..
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) {
- int ret;
- _cdns_enable_interrupt(cdns);
- ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
- if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
I was expecting cdns_update_config() to be invoked here??
- return ret;
- return 0;
It would be better if we return a value here a not success always
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0;
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
return cdns_update_config()
On 8/2/19 7:03 AM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) {
- int ret;
- _cdns_enable_interrupt(cdns);
- ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
- if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
I was expecting cdns_update_config() to be invoked here??
- return ret;
- return 0;
It would be better if we return a value here a not success always
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0;
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
return cdns_update_config()
yes, all of this is fixed already.
On Thu, Jul 25, 2019 at 06:40:01PM -0500, Pierre-Louis Bossart wrote:
Per the hardware documentation, all changes to MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL need to be validated with a self-clearing write to MCP_CONFIG_UPDATE.
For some reason, the existing code only does this write to CONFIG_UPDATE when enabling interrupts. Add a helper and do the update when the CONFIG is changed.
the sequence of intel_probe is as follows: 1. intel_link_power_up 2. intel_shim_init 3. sdw_cdns_init 4. sdw_cdns_enable_interrupt
Since we do self-clearing write to MCP_CONFIG_UPDATE in sdw_cdns_enable_interrupt once for all the config changes, we dont perform it as part of sdw_cdns_init.
It does make sense to seperate it out from sdw_cdns_enable_interrupt so that we can use when clockstop is enabled where we dont need to enable interrupts.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 9f611a1fff0a..eb46cf651d62 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -224,6 +224,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; }
+/*
- all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL
- need to be confirmed with a write to MCP_CONFIG_UPDATE
- */
+static int cdns_update_config(struct sdw_cdns *cdns) +{
- int ret;
- ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
- if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
- return ret;
+}
/*
- debugfs
*/ @@ -758,15 +774,9 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) */ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) {
int ret;
_cdns_enable_interrupt(cdns);
ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE,
CDNS_MCP_CONFIG_UPDATE_BIT);
if (ret < 0)
dev_err(cdns->dev, "Config update timedout\n");
return ret;
- return 0;
} EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@@ -943,7 +953,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
- return 0;
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
} EXPORT_SYMBOL(sdw_cdns_init);
-- 2.20.1
--
Now that we've removed the update config, there's no need for a wrapper.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index eb46cf651d62..e85e49340986 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -749,7 +749,12 @@ EXPORT_SYMBOL(sdw_cdns_thread); /* * init routines */ -static int _cdns_enable_interrupt(struct sdw_cdns *cdns) + +/** + * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config + * @cdns: Cadence instance + */ +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) { u32 mask;
@@ -767,17 +772,6 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns)
return 0; } - -/** - * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config - * @cdns: Cadence instance - */ -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) -{ - _cdns_enable_interrupt(cdns); - - return 0; -} EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
static int cdns_allocate_pdi(struct sdw_cdns *cdns,
The bus clash interrupts are generated when the status is one, and also cleared by writing a one. It's overkill/useless to use an OR when the bit is already set.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index e85e49340986..bdc3ed844829 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -692,7 +692,6 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) if (int_status & CDNS_MCP_INT_CTRL_CLASH) { /* Slave is driving bit slot during control word */ dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); - int_status |= CDNS_MCP_INT_CTRL_CLASH; }
if (int_status & CDNS_MCP_INT_DATA_CLASH) { @@ -701,7 +700,6 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) * ownership of data bits or Slave gone bonkers */ dev_err_ratelimited(cdns->dev, "Bus clash for data word\n"); - int_status |= CDNS_MCP_INT_DATA_CLASH; }
if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
The bus clash interrupts are generated when the status is one, and also cleared by writing a one. It's overkill/useless to use an OR when the bit is already set.
IIRC we were supposed to have different variable and that was the reason for setting, yes should be removed.
I have applied this one as well
Adding missing interrupt masks (parity, etc) and missing checks. Clarify which masks are for which usage.
Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index bdc3ed844829..0f3b9c160b01 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -76,9 +76,12 @@ #define CDNS_MCP_INT_DPINT BIT(11) #define CDNS_MCP_INT_CTRL_CLASH BIT(10) #define CDNS_MCP_INT_DATA_CLASH BIT(9) +#define CDNS_MCP_INT_PARITY BIT(8) #define CDNS_MCP_INT_CMD_ERR BIT(7) +#define CDNS_MCP_INT_RX_NE BIT(3) #define CDNS_MCP_INT_RX_WL BIT(2) #define CDNS_MCP_INT_TXE BIT(1) +#define CDNS_MCP_INT_TXF BIT(0)
#define CDNS_MCP_INTSET 0x4C
@@ -689,6 +692,11 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) } }
+ if (int_status & CDNS_MCP_INT_PARITY) { + /* Parity error detected by Master */ + dev_err_ratelimited(cdns->dev, "Parity error\n"); + } + if (int_status & CDNS_MCP_INT_CTRL_CLASH) { /* Slave is driving bit slot during control word */ dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); @@ -761,10 +769,21 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, CDNS_MCP_SLAVE_INTMASK1_MASK);
+ /* enable detection of slave state changes */ mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT | - CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH | - CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | - CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT; + CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH; + + /* enable detection of bus issues */ + mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | + CDNS_MCP_INT_PARITY; + + /* no detection of port interrupts for now */ + + /* enable detection of RX fifo level */ + mask |= CDNS_MCP_INT_RX_WL; + + /* now enable all of the above */ + mask |= CDNS_MCP_INT_IRQ;
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Adding missing interrupt masks (parity, etc) and missing checks. Clarify which masks are for which usage.
Signed-off-by: Bard Liao yung-chuan.liao@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index bdc3ed844829..0f3b9c160b01 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -76,9 +76,12 @@ #define CDNS_MCP_INT_DPINT BIT(11) #define CDNS_MCP_INT_CTRL_CLASH BIT(10) #define CDNS_MCP_INT_DATA_CLASH BIT(9) +#define CDNS_MCP_INT_PARITY BIT(8) #define CDNS_MCP_INT_CMD_ERR BIT(7) +#define CDNS_MCP_INT_RX_NE BIT(3) #define CDNS_MCP_INT_RX_WL BIT(2) #define CDNS_MCP_INT_TXE BIT(1) +#define CDNS_MCP_INT_TXF BIT(0)
#define CDNS_MCP_INTSET 0x4C
@@ -689,6 +692,11 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) } }
- if (int_status & CDNS_MCP_INT_PARITY) {
/* Parity error detected by Master */
dev_err_ratelimited(cdns->dev, "Parity error\n");
- }
- if (int_status & CDNS_MCP_INT_CTRL_CLASH) { /* Slave is driving bit slot during control word */ dev_err_ratelimited(cdns->dev, "Bus clash for control word\n");
@@ -761,10 +769,21 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, CDNS_MCP_SLAVE_INTMASK1_MASK);
- /* enable detection of slave state changes */ mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH |
CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT;
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
- /* enable detection of bus issues */
- mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
- /* no detection of port interrupts for now */
- /* enable detection of RX fifo level */
- mask |= CDNS_MCP_INT_RX_WL;
- /* now enable all of the above */
I think this comment seems is at wrong line..?
mask |= CDNS_MCP_INT_IRQ;
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
-- 2.20.1
@@ -761,10 +769,21 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, CDNS_MCP_SLAVE_INTMASK1_MASK);
- /* enable detection of slave state changes */ mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH |
CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT;
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
- /* enable detection of bus issues */
- mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
- /* no detection of port interrupts for now */
- /* enable detection of RX fifo level */
- mask |= CDNS_MCP_INT_RX_WL;
- /* now enable all of the above */
I think this comment seems is at wrong line..?
mask |= CDNS_MCP_INT_IRQ;
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
No it's at the right place.
This flag gates all others, if its value is zero then the value of all other bits is irrelevant.
that's what I meant by 'all of the above'.
wrong prefix and wrong macro.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 0f3b9c160b01..d9d9e3d964dd 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -85,8 +85,8 @@
#define CDNS_MCP_INTSET 0x4C
-#define CDNS_SDW_SLAVE_STAT 0x50 -#define CDNS_MCP_SLAVE_STAT_MASK BIT(1, 0) +#define CDNS_MCP_SLAVE_STAT 0x50 +#define CDNS_MCP_SLAVE_STAT_MASK GENMASK(1, 0)
#define CDNS_MCP_SLAVE_INTSTAT0 0x54 #define CDNS_MCP_SLAVE_INTSTAT1 0x58
Two off-by-one errors: INTSTAT0 missed BIT(31) and INTSTAT1 is only defined on first 16 bits.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d9d9e3d964dd..889fa2cd49ae 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -100,8 +100,8 @@ #define CDNS_MCP_SLAVE_INTMASK0 0x5C #define CDNS_MCP_SLAVE_INTMASK1 0x60
-#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(30, 0) -#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(16, 0) +#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(31, 0) +#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(15, 0)
#define CDNS_MCP_PORT_INTSTAT 0x64 #define CDNS_MCP_PDI_STAT 0x6C
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Two off-by-one errors: INTSTAT0 missed BIT(31) and INTSTAT1 is only defined on first 16 bits.
Applied, thanks
When a Slave reports multiple status in the sticky bits, find the latest configuration from the mirror of the PING frame status and update the status directly.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 34 ++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 889fa2cd49ae..25d5c7267c15 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -643,13 +643,35 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */ if (set_status > 1) { + u32 val; + dev_warn_ratelimited(cdns->dev, - "Slave reported multiple Status: %d\n", - mask); - /* - * TODO: we need to reread the status here by - * issuing a PING cmd - */ + "Slave %d reported multiple Status: %d\n", + i, mask); + + /* re-check latest status extracted from PING commands */ + val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); + val >>= (i * 2); + + switch (val & 0x3) { + case 0: + status[i] = SDW_SLAVE_UNATTACHED; + break; + case 1: + status[i] = SDW_SLAVE_ATTACHED; + break; + case 2: + status[i] = SDW_SLAVE_ALERT; + break; + default: + status[i] = SDW_SLAVE_RESERVED; + break; + } + + dev_warn_ratelimited(cdns->dev, + "Slave %d status updated to %d\n", + i, status[i]); + } }
On Thu, Jul 25, 2019 at 06:40:07PM -0500, Pierre-Louis Bossart wrote:
When a Slave reports multiple status in the sticky bits, find the latest configuration from the mirror of the PING frame status and update the status directly.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 34 ++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 889fa2cd49ae..25d5c7267c15 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -643,13 +643,35 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */ if (set_status > 1) {
u32 val;
dev_warn_ratelimited(cdns->dev,
"Slave reported multiple Status: %d\n",
mask);
/*
* TODO: we need to reread the status here by
* issuing a PING cmd
*/
"Slave %d reported multiple Status: %d\n",
i, mask);
/* re-check latest status extracted from PING commands */
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
Superfluous parentheses.
switch (val & 0x3) {
case 0:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
default:
There aren't many values left for the "default" case :-) But I'm not sure whether any of
+ case 3:
or
+ case 3: + default:
would improve readability.
Thanks Guennadi
status[i] = SDW_SLAVE_RESERVED;
break;
}
dev_warn_ratelimited(cdns->dev,
"Slave %d status updated to %d\n",
i, status[i]);
- } }
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 889fa2cd49ae..25d5c7267c15 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -643,13 +643,35 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */ if (set_status > 1) {
u32 val;
dev_warn_ratelimited(cdns->dev,
"Slave reported multiple Status: %d\n",
mask);
/*
* TODO: we need to reread the status here by
* issuing a PING cmd
*/
"Slave %d reported multiple Status: %d\n",
i, mask);
/* re-check latest status extracted from PING commands */
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
Superfluous parentheses.
Humm, I don't know my left from my right and I can't remember operator precedence, so i'd rather make it explicit...
switch (val & 0x3) {
case 0:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
default:
There aren't many values left for the "default" case :-) But I'm not sure whether any of
case 3:
or
case 3:
default:
would improve readability.
Thanks Guennadi
status[i] = SDW_SLAVE_RESERVED;
break;
Yes, those defaults are annoying. Some tools complain so I tend to leave them.
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
When a Slave reports multiple status in the sticky bits, find the latest configuration from the mirror of the PING frame status and update the status directly.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 34 ++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 889fa2cd49ae..25d5c7267c15 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -643,13 +643,35 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */ if (set_status > 1) {
u32 val;
dev_warn_ratelimited(cdns->dev,
"Slave reported multiple Status: %d\n",
mask);
/*
* TODO: we need to reread the status here by
* issuing a PING cmd
*/
"Slave %d reported multiple Status: %d\n",
i, mask);
/* re-check latest status extracted from PING commands */
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
switch (val & 0x3) {
case 0:
why not case CDNS_MCP_SLAVE_INTSTAT_NPRESENT:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
default:
status[i] = SDW_SLAVE_RESERVED;
break;
}
we have same logic in the code block preceding this, maybe good idea to write a helper and use for both
Also IIRC we can have multiple status set right?
On 8/2/19 7:20 AM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
When a Slave reports multiple status in the sticky bits, find the latest configuration from the mirror of the PING frame status and update the status directly.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 34 ++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 889fa2cd49ae..25d5c7267c15 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -643,13 +643,35 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
/* first check if Slave reported multiple status */ if (set_status > 1) {
u32 val;
dev_warn_ratelimited(cdns->dev,
"Slave reported multiple Status: %d\n",
mask);
/*
* TODO: we need to reread the status here by
* issuing a PING cmd
*/
"Slave %d reported multiple Status: %d\n",
i, mask);
/* re-check latest status extracted from PING commands */
val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
val >>= (i * 2);
switch (val & 0x3) {
case 0:
why not case CDNS_MCP_SLAVE_INTSTAT_NPRESENT:
ok
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
default:
status[i] = SDW_SLAVE_RESERVED;
break;
}
we have same logic in the code block preceding this, maybe good idea to write a helper and use for both
Yes, I am thinking about this. There are multiple cases where we want to re-check the status and clear some bits, so helpers would be good.
Also IIRC we can have multiple status set right?
Yes, the status bits are sticky and mirror all values reported in PING frames. I am still working on how to clear those bits, there are cases where we clear bits and end-up never hearing from that device ever again. classic edge/level issue I suppose.
On 02-08-19, 10:29, Pierre-Louis Bossart wrote:
On 8/2/19 7:20 AM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
default:
status[i] = SDW_SLAVE_RESERVED;
break;
}
we have same logic in the code block preceding this, maybe good idea to write a helper and use for both
Yes, I am thinking about this. There are multiple cases where we want to re-check the status and clear some bits, so helpers would be good.
Also IIRC we can have multiple status set right?
Yes, the status bits are sticky and mirror all values reported in PING frames. I am still working on how to clear those bits, there are cases where we clear bits and end-up never hearing from that device ever again. classic edge/level issue I suppose.
Then the case logic above doesn't work, it should be like the code block preceding this..
Thanks
On 8/2/19 11:01 AM, Vinod Koul wrote:
On 02-08-19, 10:29, Pierre-Louis Bossart wrote:
On 8/2/19 7:20 AM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
status[i] = SDW_SLAVE_UNATTACHED;
break;
case 1:
status[i] = SDW_SLAVE_ATTACHED;
break;
case 2:
status[i] = SDW_SLAVE_ALERT;
break;
default:
status[i] = SDW_SLAVE_RESERVED;
break;
}
we have same logic in the code block preceding this, maybe good idea to write a helper and use for both
Yes, I am thinking about this. There are multiple cases where we want to re-check the status and clear some bits, so helpers would be good.
Also IIRC we can have multiple status set right?
Yes, the status bits are sticky and mirror all values reported in PING frames. I am still working on how to clear those bits, there are cases where we clear bits and end-up never hearing from that device ever again. classic edge/level issue I suppose.
Then the case logic above doesn't work, it should be like the code block preceding this..
what I was referring to already is a problem even in single status mode.
Let's say for example that a device shows up as Device0, then we try to enumerate it and program a non-zero device number. If that fails, we currently clear the Attached status for Device0, so will never have an interrupt ever again. The device is there, attached as Device0, but we've lost the single opportunity to make it usable. The link is in most cases going to be extremely reliable, but if we know of state machines that lead to a terminal state then we should proactively have a recovery mechanism to avoid complicated debug down the road for cases where the hardware has transient issues.
For the multiple status case, we will have to look at the details and figure out which of the flags get cleared and which ones don't.
One certainty is that we absolutely have to track IO errors in interrupt context. They are recoverable in regular context but not quite in interrupt context if we clear the status bits unconditionally.
Maybe a tangent here but to be transparent there are really multiple topics we are tracking at the moment:
1. error handling in interrupts. I found a leak where if a device goes in the weeds while we program its device number and resynchronizes then we allocate a new device number instead of reusing the initial one. The bit clearing is also to be checked as explained above.
2. module dependencies: there is a race condition leading to a kernel oops if the Slave probe is not complete before the .update_status is invoked.
3. jack detection. The jack detection routine is called as a result of an imp-def Slave interrupt. We never documented the assumption that if this jack detection takes time then it needs to be done offline, e.g. in a work queue. Or if we still want it to be done in a the interrupt thread then we need to re-enable interrupts earlier, otherwise one device can stop interrupt handling for a fairly long duration.
4. streaming stop on link errors. We've seen in tests that if you reset the link or a Slave device with debugfs while audio is playing then streaming continues. This condition could happen if a device loses sync, and the spec says the Slave needs to reset its channel enable bits. At the command level, we handle this situation and will recover, but there is no notification to the ALSA layers to try and recover on the PCM side of things (as if it were an underflow condition). We also try to disable a stream but get all kinds of errors since it's lost state.
All of those points are corner cases but they are important to solve for actual products.
Enable interrupts first, then engage hardware bus reset with maximum duration to make sure the Slave(s) correctly detect the reset pattern and to ensure electrical conflicts can be resolved.
Without these changes the initialization is randomly corrupted by bus clashes, parity errors and Slave attachment does not generate any interrupt, despite the status showing them being attached.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 35 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 25d5c7267c15..442f78c00f09 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -778,6 +778,31 @@ EXPORT_SYMBOL(sdw_cdns_thread); * init routines */
+static int do_reset(struct sdw_cdns *cdns) +{ + int ret; + + /* program maximum length reset to be safe */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_RST_DELAY, + CDNS_MCP_CONTROL_RST_DELAY); + + /* use hardware generated reset */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_HW_RST, + CDNS_MCP_CONTROL_HW_RST); + + /* enable bus operations with clock and data */ + cdns_updatel(cdns, CDNS_MCP_CONFIG, + CDNS_MCP_CONFIG_OP, + CDNS_MCP_CONFIG_OP_NORMAL); + + /* commit changes */ + ret = cdns_update_config(cdns); + + return ret; +} + /** * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config * @cdns: Cadence instance @@ -809,7 +834,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
- return 0; + return do_reset(cdns); } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@@ -958,6 +983,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL);
+ /* flush command FIFOs */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, + CDNS_MCP_CONTROL_CMD_RST); + /* Set cmd accept mode */ cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, CDNS_MCP_CONTROL_CMD_ACCEPT); @@ -980,10 +1009,6 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set cmd mode for Tx and Rx cmds */ val &= ~CDNS_MCP_CONFIG_CMD;
- /* Set operation to normal */ - val &= ~CDNS_MCP_CONFIG_OP; - val |= CDNS_MCP_CONFIG_OP_NORMAL; - cdns_writel(cdns, CDNS_MCP_CONFIG, val);
/* commit changes */
On Thu, Jul 25, 2019 at 06:40:08PM -0500, Pierre-Louis Bossart wrote:
Enable interrupts first, then engage hardware bus reset with maximum duration to make sure the Slave(s) correctly detect the reset pattern and to ensure electrical conflicts can be resolved.
Without these changes the initialization is randomly corrupted by bus clashes, parity errors and Slave attachment does not generate any interrupt, despite the status showing them being attached.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 35 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 25d5c7267c15..442f78c00f09 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -778,6 +778,31 @@ EXPORT_SYMBOL(sdw_cdns_thread);
- init routines
*/
+static int do_reset(struct sdw_cdns *cdns) +{
- int ret;
- /* program maximum length reset to be safe */
- cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_RST_DELAY,
CDNS_MCP_CONTROL_RST_DELAY);
- /* use hardware generated reset */
- cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_HW_RST,
CDNS_MCP_CONTROL_HW_RST);
- /* enable bus operations with clock and data */
- cdns_updatel(cdns, CDNS_MCP_CONFIG,
CDNS_MCP_CONFIG_OP,
CDNS_MCP_CONFIG_OP_NORMAL);
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
+ return cdns_update_config(cdns);
and remove the "ret" variable.
Thanks Guennadi
+}
/**
- sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
- @cdns: Cadence instance
@@ -809,7 +834,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
- return 0;
- return do_reset(cdns);
} EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
@@ -958,6 +983,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL);
- /* flush command FIFOs */
- cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST,
CDNS_MCP_CONTROL_CMD_RST);
- /* Set cmd accept mode */ cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, CDNS_MCP_CONTROL_CMD_ACCEPT);
@@ -980,10 +1009,6 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set cmd mode for Tx and Rx cmds */ val &= ~CDNS_MCP_CONFIG_CMD;
/* Set operation to normal */
val &= ~CDNS_MCP_CONFIG_OP;
val |= CDNS_MCP_CONFIG_OP_NORMAL;
cdns_writel(cdns, CDNS_MCP_CONFIG, val);
/* commit changes */
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
+static int do_reset(struct sdw_cdns *cdns) +{
- int ret;
- /* program maximum length reset to be safe */
- cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_RST_DELAY,
CDNS_MCP_CONTROL_RST_DELAY);
- /* use hardware generated reset */
- cdns_updatel(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_HW_RST,
CDNS_MCP_CONTROL_HW_RST);
- /* enable bus operations with clock and data */
- cdns_updatel(cdns, CDNS_MCP_CONFIG,
CDNS_MCP_CONFIG_OP,
CDNS_MCP_CONFIG_OP_NORMAL);
- /* commit changes */
- ret = cdns_update_config(cdns);
- return ret;
- return cdns_update_config(cdns);
and remove the "ret" variable.
Yes, it's the same issue as previously reported. will fix.
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; + if (pm_runtime_enabled(slave->bus->dev)) { + ret = pm_runtime_get_sync(slave->bus->dev); + if (ret < 0) + return ret; + }
ret = sdw_transfer(slave->bus, &msg); - pm_runtime_put(slave->bus->dev); + + if (pm_runtime_enabled(slave->bus->dev)) + pm_runtime_put(slave->bus->dev);
return ret; } @@ -359,13 +363,16 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) slave->dev_num, SDW_MSG_FLAG_WRITE, val); if (ret < 0) return ret; - - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; + if (pm_runtime_enabled(slave->bus->dev)) { + ret = pm_runtime_get_sync(slave->bus->dev); + if (ret < 0) + return ret; + }
ret = sdw_transfer(slave->bus, &msg); - pm_runtime_put(slave->bus->dev); + + if (pm_runtime_enabled(slave->bus->dev)) + pm_runtime_put(slave->bus->dev);
return ret; }
Hi Pierre,
I might be wrong but this doesn't seem right to me. (Supposedly) all RT-PM functions check for "enabled" internally. The only thing that can happen is that if RT-PM isn't enabled some of those functions will return an error. So, in those cases where the return value of RT-PM functions isn't checked, I don't think you need to do anything. Where it is checked maybe do
+ if (ret < 0 && pm_runtime_enabled(slave->bus->dev))
Thanks Guennadi
On Thu, Jul 25, 2019 at 06:40:09PM -0500, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
} @@ -359,13 +363,16 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) slave->dev_num, SDW_MSG_FLAG_WRITE, val); if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
}
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Hello,
I while back I proposed a patch for this, but it went nowhere.
https://patchwork.kernel.org/patch/10887405/ Maybe something similar can be implemented?
Jan
On 26 Jul 2019, at 09:39, Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com wrote:
EXTERNAL MAIL
Hi Pierre,
I might be wrong but this doesn't seem right to me. (Supposedly) all RT-PM functions check for "enabled" internally. The only thing that can happen is that if RT-PM isn't enabled some of those functions will return an error. So, in those cases where the return value of RT-PM functions isn't checked, I don't think you need to do anything. Where it is checked maybe do
- if (ret < 0 && pm_runtime_enabled(slave->bus->dev))
Thanks Guennadi
On Thu, Jul 25, 2019 at 06:40:09PM -0500, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
} @@ -359,13 +363,16 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) slave->dev_num, SDW_MSG_FLAG_WRITE, val); if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
}
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://urldefense.proofpoint.com/v2/url?u=https-3A__mailman.alsa-2Dproject....
Hi Jan,
On Fri, Jul 26, 2019 at 07:47:04AM +0000, Jan Kotas wrote:
Hello,
I while back I proposed a patch for this, but it went nowhere.
https://patchwork.kernel.org/patch/10887405/ Maybe something similar can be implemented?
Yes, I was thinking about checkint -EACCESS too, but then I noticed this code in rpm_resume():
else if (dev->power.disable_depth == 1 && dev->power.is_suspended && dev->power.runtime_status == RPM_ACTIVE) retval = 1;
i.e. if RT-PM is disabled on the device (but only exactly once?..) and it's active and the device is suspended for a system suspend, the function will return 1. I don't understand the logic of this code, but it seems to me it could break the -EACCESS check?
Thanks Guennadi
Jan
On 26 Jul 2019, at 09:39, Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com wrote:
EXTERNAL MAIL
Hi Pierre,
I might be wrong but this doesn't seem right to me. (Supposedly) all RT-PM functions check for "enabled" internally. The only thing that can happen is that if RT-PM isn't enabled some of those functions will return an error. So, in those cases where the return value of RT-PM functions isn't checked, I don't think you need to do anything. Where it is checked maybe do
- if (ret < 0 && pm_runtime_enabled(slave->bus->dev))
Thanks Guennadi
On Thu, Jul 25, 2019 at 06:40:09PM -0500, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
} @@ -359,13 +363,16 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) slave->dev_num, SDW_MSG_FLAG_WRITE, val); if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
}
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://urldefense.proofpoint.com/v2/url?u=https-3A__mailman.alsa-2Dproject....
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On 26 Jul 2019, at 10:22, Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com wrote:
EXTERNAL MAIL
Hi Jan,
On Fri, Jul 26, 2019 at 07:47:04AM +0000, Jan Kotas wrote:
Hello,
I while back I proposed a patch for this, but it went nowhere.
https://urldefense.proofpoint.com/v2/url?u=https-3A__patchwork.kernel.org_pa... Maybe something similar can be implemented?
Yes, I was thinking about checkint -EACCESS too, but then I noticed this code in rpm_resume():
else if (dev->power.disable_depth == 1 && dev->power.is_suspended && dev->power.runtime_status == RPM_ACTIVE) retval = 1;
i.e. if RT-PM is disabled on the device (but only exactly once?..) and it's active and the device is suspended for a system suspend, the function will return 1. I don't understand the logic of this code, but it seems to me it could break the -EACCESS check?
Hi,
In such case ret < 0 will not be true, which I think is fine, if I’m understanding you correctly.
Regards, Jan
Thanks Guennadi
Jan
On 26 Jul 2019, at 09:39, Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com wrote:
EXTERNAL MAIL
Hi Pierre,
I might be wrong but this doesn't seem right to me. (Supposedly) all RT-PM functions check for "enabled" internally. The only thing that can happen is that if RT-PM isn't enabled some of those functions will return an error. So, in those cases where the return value of RT-PM functions isn't checked, I don't think you need to do anything. Where it is checked maybe do
- if (ret < 0 && pm_runtime_enabled(slave->bus->dev))
Thanks Guennadi
On Thu, Jul 25, 2019 at 06:40:09PM -0500, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
} @@ -359,13 +363,16 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) slave->dev_num, SDW_MSG_FLAG_WRITE, val); if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
}
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://urldefense.proofpoint.com/v2/url?u=https-3A__mailman.alsa-2Dproject....
Alsa-devel mailing list Alsa-devel@alsa-project.org https://urldefense.proofpoint.com/v2/url?u=https-3A__mailman.alsa-2Dproject....
On Fri, Jul 26, 2019 at 08:33:35AM +0000, Jan Kotas wrote:
On 26 Jul 2019, at 10:22, Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com wrote:
EXTERNAL MAIL
Hi Jan,
On Fri, Jul 26, 2019 at 07:47:04AM +0000, Jan Kotas wrote:
Hello,
I while back I proposed a patch for this, but it went nowhere.
https://urldefense.proofpoint.com/v2/url?u=https-3A__patchwork.kernel.org_pa... Maybe something similar can be implemented?
Yes, I was thinking about checkint -EACCESS too, but then I noticed this code in rpm_resume():
else if (dev->power.disable_depth == 1 && dev->power.is_suspended && dev->power.runtime_status == RPM_ACTIVE) retval = 1;
i.e. if RT-PM is disabled on the device (but only exactly once?..) and it's active and the device is suspended for a system suspend, the function will return 1. I don't understand the logic of this code, but it seems to me it could break the -EACCESS check?
Hi,
In such case ret < 0 will not be true, which I think is fine, if I’m understanding you correctly.
Yes, if we just have to distinguish a single case "RT-PM is enabled and it failed." Which is indeed the case here, it seems. However if we want to check whether RT-PM is disabled after a call to, say, pm_runtime_get_sync(), then just checking -EACCESS isn't always enough - there can be cases when RT-PM is disabled and the return code is 1. But yes, just for checking for failures, like here, it should be fine.
Thanks Guennadi
Jan
On 26 Jul 2019, at 09:39, Guennadi Liakhovetski guennadi.liakhovetski@linux.intel.com wrote:
EXTERNAL MAIL
Hi Pierre,
I might be wrong but this doesn't seem right to me. (Supposedly) all RT-PM functions check for "enabled" internally. The only thing that can happen is that if RT-PM isn't enabled some of those functions will return an error. So, in those cases where the return value of RT-PM functions isn't checked, I don't think you need to do anything. Where it is checked maybe do
- if (ret < 0 && pm_runtime_enabled(slave->bus->dev))
Thanks Guennadi
On Thu, Jul 25, 2019 at 06:40:09PM -0500, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
} @@ -359,13 +363,16 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) slave->dev_num, SDW_MSG_FLAG_WRITE, val); if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
return ret;
}
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://urldefense.proofpoint.com/v2/url?u=https-3A__mailman.alsa-2Dproject....
Alsa-devel mailing list Alsa-devel@alsa-project.org https://urldefense.proofpoint.com/v2/url?u=https-3A__mailman.alsa-2Dproject....
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
This thread became unreadable with interleaved top-posting, allow me restate the options and ask PM folks what they think
On 7/25/19 6:40 PM, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
}
ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
- if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
This is option1: we explicitly test if pm_runtime is enabled before calling _get_sync() and _put()
option2 (suggested by Jan Kotas): catch the -EACCESS error code
ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) + if (ret < 0 && ret != -EACCES) return ret;
option3: ignore the return value as done in quite a few drivers
Are there any other options? I am personally surprised this is not handled in the pm_runtime core, not sure why users need to test for this?
Thanks Pierre
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
This thread became unreadable with interleaved top-posting, allow me restate the options and ask PM folks what they think
On 7/25/19 6:40 PM, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Suggested-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5ad4109dc72f..0a45dc5713df 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -332,12 +332,16 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
- if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
return ret;
- } ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
- if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
This is option1: we explicitly test if pm_runtime is enabled before calling _get_sync() and _put()
option2 (suggested by Jan Kotas): catch the -EACCESS error code
ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
- if (ret < 0 && ret != -EACCES) return ret;
option3: ignore the return value as done in quite a few drivers
Are there any other options? I am personally surprised this is not handled in the pm_runtime core, not sure why users need to test for this?
option 4: fix this in runtime PM :-) This seems like the best option to me, but probably not the easiest one. Otherwise I'd go with (2), I think, since that's also the official purpose of the -EACCESS return code:
https://lists.linuxfoundation.org/pipermail/linux-pm/2011-June/031930.html
Thanks Guennadi
On Fri, Jul 26, 2019 at 08:25:35PM +0200, Guennadi Liakhovetski wrote:
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
On 7/25/19 6:40 PM, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
option2 (suggested by Jan Kotas): catch the -EACCESS error code
ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
- if (ret < 0 && ret != -EACCES) return ret;
Otherwise I'd go with (2), I think, since that's also the official purpose of the -EACCESS return code:
https://lists.linuxfoundation.org/pipermail/linux-pm/2011-June/031930.html
And at least we have examples in the kernel
drivers/gpu/drm/radeon/radeon_fb.c:57: if (ret < 0 && ret != -EACCES) {
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
This thread became unreadable with interleaved top-posting, allow me restate the options and ask PM folks what they think
On 7/25/19 6:40 PM, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Just a side note below...
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
Here...
return ret;
- if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
...and thus here...
return ret;
- } ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
- if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
This is option1: we explicitly test if pm_runtime is enabled before calling _get_sync() and _put()
option2 (suggested by Jan Kotas): catch the -EACCESS error code
ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
- if (ret < 0 && ret != -EACCES)
...and here, the pm_runtime_put_noidle() call is missed.
return ret;
option3: ignore the return value as done in quite a few drivers
Are there any other options? I am personally surprised this is not handled in the pm_runtime core, not sure why users need to test for this?
On 7/26/19 2:08 PM, Andy Shevchenko wrote:
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
This thread became unreadable with interleaved top-posting, allow me restate the options and ask PM folks what they think
On 7/25/19 6:40 PM, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
Just a side note below...
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
Here...
return ret;
- if (pm_runtime_enabled(slave->bus->dev)) {
ret = pm_runtime_get_sync(slave->bus->dev);
if (ret < 0)
...and thus here...
return ret;
- } ret = sdw_transfer(slave->bus, &msg);
- pm_runtime_put(slave->bus->dev);
- if (pm_runtime_enabled(slave->bus->dev))
pm_runtime_put(slave->bus->dev);
This is option1: we explicitly test if pm_runtime is enabled before calling _get_sync() and _put()
option2 (suggested by Jan Kotas): catch the -EACCESS error code
ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
- if (ret < 0 && ret != -EACCES)
...and here, the pm_runtime_put_noidle() call is missed.
yes but in the example you provided, they actually do more work than just decrement the device usage counter:
static int radeonfb_open(struct fb_info *info, int user) { struct radeon_fbdev *rfbdev = info->par; struct radeon_device *rdev = rfbdev->rdev; int ret = pm_runtime_get_sync(rdev->ddev->dev); if (ret < 0 && ret != -EACCES) { pm_runtime_mark_last_busy(rdev->ddev->dev); pm_runtime_put_autosuspend(rdev->ddev->dev); return ret; } return 0; }
unless I am missing something pm_runtime_put_noidle() and _put_autosuspend() are not equivalent, are they?
On Mon, Jul 29, 2019 at 05:07:39PM -0500, Pierre-Louis Bossart wrote:
On 7/26/19 2:08 PM, Andy Shevchenko wrote:
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
- if (ret < 0)
- if (ret < 0 && ret != -EACCES)
...and here, the pm_runtime_put_noidle() call is missed.
yes but in the example you provided, they actually do more work than just decrement the device usage counter:
In their case they would like to do that. You decide what is appropriate call in your case.
My point is, that reference counter in case of error handling should be returned back to its value.
On 7/30/19 6:21 AM, Andy Shevchenko wrote:
On Mon, Jul 29, 2019 at 05:07:39PM -0500, Pierre-Louis Bossart wrote:
On 7/26/19 2:08 PM, Andy Shevchenko wrote:
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
- if (ret < 0)
- if (ret < 0 && ret != -EACCES)
...and here, the pm_runtime_put_noidle() call is missed.
yes but in the example you provided, they actually do more work than just decrement the device usage counter:
In their case they would like to do that. You decide what is appropriate call in your case.
My point is, that reference counter in case of error handling should be returned back to its value.
Agree on the reference count. I am however not clear on the next step and 'what is appropriate'.
If pm_runtime_get_sync() failed, then technically the device was not resumed so marking it as last_busy+autosuspend, or using a plain vanilla put() will not result in any action. I must be missing something here.
On Tue, Jul 30, 2019 at 07:57:46AM -0500, Pierre-Louis Bossart wrote:
On 7/30/19 6:21 AM, Andy Shevchenko wrote:
On Mon, Jul 29, 2019 at 05:07:39PM -0500, Pierre-Louis Bossart wrote:
On 7/26/19 2:08 PM, Andy Shevchenko wrote:
On Fri, Jul 26, 2019 at 01:08:57PM -0500, Pierre-Louis Bossart wrote:
- if (ret < 0)
- if (ret < 0 && ret != -EACCES)
...and here, the pm_runtime_put_noidle() call is missed.
yes but in the example you provided, they actually do more work than just decrement the device usage counter:
In their case they would like to do that. You decide what is appropriate call in your case.
My point is, that reference counter in case of error handling should be returned back to its value.
Agree on the reference count. I am however not clear on the next step and 'what is appropriate'.
If pm_runtime_get_sync() failed, then technically the device was not resumed
Not so straight. It depends on reference count. It might be true (most cases I think), or not true, if device had been resumed previously by other call.
so marking it as last_busy+autosuspend, or using a plain vanilla put() will not result in any action. I must be missing something here.
put_noidle(). Because if it failed on the first call and was resumed, put() will try to shut it down (since reference count goes to no-user base).
On Tue, Jul 30, 2019 at 06:58:09PM +0300, Andy Shevchenko wrote:
On Tue, Jul 30, 2019 at 07:57:46AM -0500, Pierre-Louis Bossart wrote:
On 7/30/19 6:21 AM, Andy Shevchenko wrote:
On Mon, Jul 29, 2019 at 05:07:39PM -0500, Pierre-Louis Bossart wrote:
On 7/26/19 2:08 PM, Andy Shevchenko wrote:
so marking it as last_busy+autosuspend, or using a plain vanilla put() will not result in any action. I must be missing something here.
put_noidle(). Because if it failed on the first call and was resumed, put() will try to shut it down (since reference count goes to no-user base).
Sorry, this has to be read as (was -> wasn't)
put_noidle(). Because if it failed on the first call and wasn't resumed, put() will try to shut it down (since reference count goes to no-user base).
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
We discussed this with Ulf sometime back and it was a consensus the core should handle it, but that may take a while.
So that led me to explore what others do notably ASoC, based on this I feel we should not check the error code. We handle the non streaming case here but streaming is handled in ASoC which doesnt check the return
Pierre, can you verify the below patch and let me know if that is fine for Intel platforms
--- >8 ---
From: Vinod Koul vkoul@kernel.org Date: Fri, 2 Aug 2019 22:15:11 +0530 Subject: [PATCH] soundwire: dont check return of pm_runtime_get_sync()
Soundwire core checks pm_runtime_get_sync() return. But in case the driver has not enabled runtime pm we get an error.
To fix this, dont check the return. We handle the non streaming case in framework but streaming case has similar handling in ASoC so make it same across use cases
Signed-off-by: Vinod Koul vkoul@kernel.org --- drivers/soundwire/bus.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fe745830a261..9cdf7e9e0852 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -326,9 +326,7 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; + pm_runtime_get_sync(slave->bus->dev);
ret = sdw_transfer(slave->bus, &msg); pm_runtime_put(slave->bus->dev); @@ -354,9 +352,7 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; + pm_runtime_get_sync(slave->bus->dev);
ret = sdw_transfer(slave->bus, &msg); pm_runtime_put(slave->bus->dev);
On 8/2/19 11:58 AM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Not all platforms support runtime_pm for now, let's use runtime_pm only when enabled.
We discussed this with Ulf sometime back and it was a consensus the core should handle it, but that may take a while.
So that led me to explore what others do notably ASoC, based on this I feel we should not check the error code. We handle the non streaming case here but streaming is handled in ASoC which doesnt check the return
Pierre, can you verify the below patch and let me know if that is fine for Intel platforms
So if for some reason we cannot resume, then we'd still initiate a transaction and have even more issues to sort out.
Fail big and fail early would really be my preference.
Also the user of this function is largely the Slave driver, which typically doesn't do any streaming operation but controls the imp-def registers. The bus driver will only use this routine for standard registers and that's a very small part of the total traffic.
--- >8 ---
From: Vinod Koul vkoul@kernel.org Date: Fri, 2 Aug 2019 22:15:11 +0530 Subject: [PATCH] soundwire: dont check return of pm_runtime_get_sync()
Soundwire core checks pm_runtime_get_sync() return. But in case the driver has not enabled runtime pm we get an error.
To fix this, dont check the return. We handle the non streaming case in framework but streaming case has similar handling in ASoC so make it same across use cases
Signed-off-by: Vinod Koul vkoul@kernel.org
drivers/soundwire/bus.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fe745830a261..9cdf7e9e0852 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -326,9 +326,7 @@ int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
pm_runtime_get_sync(slave->bus->dev);
ret = sdw_transfer(slave->bus, &msg); pm_runtime_put(slave->bus->dev);
@@ -354,9 +352,7 @@ int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) if (ret < 0) return ret;
- ret = pm_runtime_get_sync(slave->bus->dev);
- if (ret < 0)
return ret;
pm_runtime_get_sync(slave->bus->dev);
ret = sdw_transfer(slave->bus, &msg); pm_runtime_put(slave->bus->dev);
Assigning a device number to a Slave will result in additional events when it reports its status in a PING frame. There is no point in dealing with all the other devices in a loop, this can be done when a non-device0 event happens.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/bus.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 0a45dc5713df..bca378806d00 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -985,6 +985,11 @@ int sdw_handle_slave_status(struct sdw_bus *bus, ret = sdw_program_device_num(bus); if (ret) dev_err(bus->dev, "Slave attach failed: %d\n", ret); + /* + * programming a device number will have side effects, + * so we deal with other devices at a later time + */ + return ret; }
/* Continue to check other slave statuses */
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Assigning a device number to a Slave will result in additional events when it reports its status in a PING frame. There is no point in dealing with all the other devices in a loop, this can be done when a non-device0 event happens.
Applied, thanks
update comments to provide better understanding of enumeration flows.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/bus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index bca378806d00..2354675ef104 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -483,7 +483,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) { - dev_err(&slave->dev, "Program device_num failed: %d\n", ret); + dev_err(&slave->dev, "Program device_num %d failed: %d\n", + dev_num, ret); return ret; }
@@ -540,6 +541,7 @@ static int sdw_program_device_num(struct sdw_bus *bus) do { ret = sdw_transfer(bus, &msg); if (ret == -ENODATA) { /* end of device id reads */ + dev_dbg(bus->dev, "No more devices to enumerate\n"); ret = 0; break; } @@ -982,6 +984,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, int i, ret = 0;
if (status[0] == SDW_SLAVE_ATTACHED) { + dev_err(bus->dev, "Slave attached, programming device number\n"); ret = sdw_program_device_num(bus); if (ret) dev_err(bus->dev, "Slave attach failed: %d\n", ret);
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
update comments to provide better understanding of enumeration flows.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index bca378806d00..2354675ef104 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -483,7 +483,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) {
dev_err(&slave->dev, "Program device_num failed: %d\n", ret);
dev_err(&slave->dev, "Program device_num %d failed: %d\n",
return ret; }dev_num, ret);
@@ -540,6 +541,7 @@ static int sdw_program_device_num(struct sdw_bus *bus) do { ret = sdw_transfer(bus, &msg); if (ret == -ENODATA) { /* end of device id reads */
}dev_dbg(bus->dev, "No more devices to enumerate\n"); ret = 0; break;
@@ -982,6 +984,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, int i, ret = 0;
if (status[0] == SDW_SLAVE_ATTACHED) {
dev_err(bus->dev, "Slave attached, programming device number\n");
This should be debug level
On 8/2/19 12:00 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
update comments to provide better understanding of enumeration flows.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index bca378806d00..2354675ef104 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -483,7 +483,8 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); if (ret < 0) {
dev_err(&slave->dev, "Program device_num failed: %d\n", ret);
dev_err(&slave->dev, "Program device_num %d failed: %d\n",
return ret; }dev_num, ret);
@@ -540,6 +541,7 @@ static int sdw_program_device_num(struct sdw_bus *bus) do { ret = sdw_transfer(bus, &msg); if (ret == -ENODATA) { /* end of device id reads */
}dev_dbg(bus->dev, "No more devices to enumerate\n"); ret = 0; break;
@@ -982,6 +984,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus, int i, ret = 0;
if (status[0] == SDW_SLAVE_ATTACHED) {
dev_err(bus->dev, "Slave attached, programming device number\n");
This should be debug level
yes, good catch, will fix.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index c0bf6ff00a44..d375bbfead18 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -165,6 +165,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
+int sdw_cdns_suspend(struct sdw_cdns *cdns); +bool sdw_cdns_check_resume_status(struct sdw_cdns *cdns); + int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir);
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index c0bf6ff00a44..d375bbfead18 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -165,6 +165,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
+int sdw_cdns_suspend(struct sdw_cdns *cdns); +bool sdw_cdns_check_resume_status(struct sdw_cdns *cdns);
- int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir);
No commit message, guess it's SQUASHME commit and shouldn't be part of overall series.
On 7/26/19 5:04 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
Signed-off-by: Pierre-Louis Bossart
pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index c0bf6ff00a44..d375bbfead18 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -165,6 +165,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); +int sdw_cdns_suspend(struct sdw_cdns *cdns); +bool sdw_cdns_check_resume_status(struct sdw_cdns *cdns);
int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir);
No commit message, guess it's SQUASHME commit and shouldn't be part of overall series.
I can't recall why it's separate, will look into this. Thanks.
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Please do provide the changelog on why this change is needed.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index c0bf6ff00a44..d375bbfead18 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -165,6 +165,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
+int sdw_cdns_suspend(struct sdw_cdns *cdns); +bool sdw_cdns_check_resume_status(struct sdw_cdns *cdns);
int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir); -- 2.20.1
On 8/2/19 12:03 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Please do provide the changelog on why this change is needed.
not needed for now, will remove.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index c0bf6ff00a44..d375bbfead18 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -165,6 +165,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
+int sdw_cdns_suspend(struct sdw_cdns *cdns); +bool sdw_cdns_check_resume_status(struct sdw_cdns *cdns);
- int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir);
-- 2.20.1
Add a prefix for common tables and export 2 helpers to set the frame shapes based on row/col values.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/bus.h | 7 +++++-- drivers/soundwire/stream.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 06ac4adb0074..c57c9c23f6ca 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,8 +73,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
-extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; +extern int sdw_rows[SDW_FRAME_ROWS]; +extern int sdw_cols[SDW_FRAME_COLS]; + +int sdw_find_row_index(int row); +int sdw_find_col_index(int col);
/** * sdw_port_runtime: Runtime port parameters for Master or Slave diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a0476755a459..53f5e790fcd7 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -21,37 +21,39 @@ * The rows are arranged as per the array index value programmed * in register. The index 15 has dummy value 0 in order to fill hole. */ -int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, +int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 96, 100, 120, 128, 150, 160, 250, 0, 192, 200, 240, 256, 72, 144, 90, 180};
-int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
-static int sdw_find_col_index(int col) +int sdw_find_col_index(int col) { int i;
for (i = 0; i < SDW_FRAME_COLS; i++) { - if (cols[i] == col) + if (sdw_cols[i] == col) return i; }
pr_warn("Requested column not found, selecting lowest column no: 2\n"); return 0; } +EXPORT_SYMBOL(sdw_find_col_index);
-static int sdw_find_row_index(int row) +int sdw_find_row_index(int row) { int i;
for (i = 0; i < SDW_FRAME_ROWS; i++) { - if (rows[i] == row) + if (sdw_rows[i] == row) return i; }
pr_warn("Requested row not found, selecting lowest row no: 48\n"); return 0; } +EXPORT_SYMBOL(sdw_find_row_index);
static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave,
On Thu, Jul 25, 2019 at 06:40:13PM -0500, Pierre-Louis Bossart wrote:
Add a prefix for common tables and export 2 helpers to set the frame shapes based on row/col values.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.h | 7 +++++-- drivers/soundwire/stream.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 06ac4adb0074..c57c9c23f6ca 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,8 +73,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
-extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; +extern int sdw_rows[SDW_FRAME_ROWS]; +extern int sdw_cols[SDW_FRAME_COLS];
So these arrays actually have to be exported? In the current (5.2) sources they seem to only be used in stream.c, maybe make them static there?
+int sdw_find_row_index(int row); +int sdw_find_col_index(int col);
/**
- sdw_port_runtime: Runtime port parameters for Master or Slave
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a0476755a459..53f5e790fcd7 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -21,37 +21,39 @@
- The rows are arranged as per the array index value programmed
- in register. The index 15 has dummy value 0 in order to fill hole.
*/ -int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, +int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 96, 100, 120, 128, 150, 160, 250, 0, 192, 200, 240, 256, 72, 144, 90, 180};
-int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
-static int sdw_find_col_index(int col) +int sdw_find_col_index(int col) { int i;
for (i = 0; i < SDW_FRAME_COLS; i++) {
if (cols[i] == col)
if (sdw_cols[i] == col) return i;
}
pr_warn("Requested column not found, selecting lowest column no: 2\n"); return 0;
} +EXPORT_SYMBOL(sdw_find_col_index);
-static int sdw_find_row_index(int row) +int sdw_find_row_index(int row) { int i;
for (i = 0; i < SDW_FRAME_ROWS; i++) {
if (rows[i] == row)
if (sdw_rows[i] == row) return i;
}
pr_warn("Requested row not found, selecting lowest row no: 48\n"); return 0;
} +EXPORT_SYMBOL(sdw_find_row_index);
static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave, -- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On 7/26/19 9:43 AM, Guennadi Liakhovetski wrote:
On Thu, Jul 25, 2019 at 06:40:13PM -0500, Pierre-Louis Bossart wrote:
Add a prefix for common tables and export 2 helpers to set the frame shapes based on row/col values.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.h | 7 +++++-- drivers/soundwire/stream.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 06ac4adb0074..c57c9c23f6ca 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,8 +73,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
-extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; +extern int sdw_rows[SDW_FRAME_ROWS]; +extern int sdw_cols[SDW_FRAME_COLS];
So these arrays actually have to be exported? In the current (5.2) sources they seem to only be used in stream.c, maybe make them static there?
+int sdw_find_row_index(int row); +int sdw_find_col_index(int col);
yes, they need to be exported, they are used by the allocation algorithm (in Patch 27). Others will need this for non-Intel solutions, it's really a part of the standard definition and should be shared. I can improve the commit message to make this explicit.
On 26-07-19, 10:26, Pierre-Louis Bossart wrote:
On 7/26/19 9:43 AM, Guennadi Liakhovetski wrote:
On Thu, Jul 25, 2019 at 06:40:13PM -0500, Pierre-Louis Bossart wrote:
Add a prefix for common tables and export 2 helpers to set the frame shapes based on row/col values.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.h | 7 +++++-- drivers/soundwire/stream.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 06ac4adb0074..c57c9c23f6ca 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,8 +73,11 @@ struct sdw_msg { #define SDW_DOUBLE_RATE_FACTOR 2 -extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; +extern int sdw_rows[SDW_FRAME_ROWS]; +extern int sdw_cols[SDW_FRAME_COLS];
So these arrays actually have to be exported? In the current (5.2) sources they seem to only be used in stream.c, maybe make them static there?
+int sdw_find_row_index(int row); +int sdw_find_col_index(int col);
yes, they need to be exported, they are used by the allocation algorithm (in Patch 27). Others will need this for non-Intel solutions, it's really a part of the standard definition and should be shared. I can improve the commit message to make this explicit.
Yes that would help! And also move it to before it's usage so it clear that it is used in next one.
On Thu, Jul 25, 2019 at 06:40:13PM -0500, Pierre-Louis Bossart wrote:
Add a prefix for common tables and export 2 helpers to set the frame shapes based on row/col values.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.h | 7 +++++-- drivers/soundwire/stream.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 06ac4adb0074..c57c9c23f6ca 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,8 +73,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
-extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; +extern int sdw_rows[SDW_FRAME_ROWS]; +extern int sdw_cols[SDW_FRAME_COLS];
+int sdw_find_row_index(int row); +int sdw_find_col_index(int col);
We use index values only in bank switch operations to program registers. Do we really need to export sdw_find_row_index & sdw_find_col_index?? If i understand correctly the allocation algorithm only needs to know about cols and rows values and not index.
/**
- sdw_port_runtime: Runtime port parameters for Master or Slave
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a0476755a459..53f5e790fcd7 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -21,37 +21,39 @@
- The rows are arranged as per the array index value programmed
- in register. The index 15 has dummy value 0 in order to fill hole.
*/ -int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, +int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 96, 100, 120, 128, 150, 160, 250, 0, 192, 200, 240, 256, 72, 144, 90, 180};
-int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
-static int sdw_find_col_index(int col) +int sdw_find_col_index(int col) { int i;
for (i = 0; i < SDW_FRAME_COLS; i++) {
if (cols[i] == col)
if (sdw_cols[i] == col) return i;
}
pr_warn("Requested column not found, selecting lowest column no: 2\n"); return 0;
} +EXPORT_SYMBOL(sdw_find_col_index);
-static int sdw_find_row_index(int row) +int sdw_find_row_index(int row) { int i;
for (i = 0; i < SDW_FRAME_ROWS; i++) {
if (rows[i] == row)
if (sdw_rows[i] == row) return i;
}
pr_warn("Requested row not found, selecting lowest row no: 48\n"); return 0;
} +EXPORT_SYMBOL(sdw_find_row_index);
static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave, -- 2.20.1
--
On 8/5/19 4:39 AM, Sanyog Kale wrote:
On Thu, Jul 25, 2019 at 06:40:13PM -0500, Pierre-Louis Bossart wrote:
Add a prefix for common tables and export 2 helpers to set the frame shapes based on row/col values.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/bus.h | 7 +++++-- drivers/soundwire/stream.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 06ac4adb0074..c57c9c23f6ca 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -73,8 +73,11 @@ struct sdw_msg {
#define SDW_DOUBLE_RATE_FACTOR 2
-extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; +extern int sdw_rows[SDW_FRAME_ROWS]; +extern int sdw_cols[SDW_FRAME_COLS];
+int sdw_find_row_index(int row); +int sdw_find_col_index(int col);
We use index values only in bank switch operations to program registers. Do we really need to export sdw_find_row_index & sdw_find_col_index?? If i understand correctly the allocation algorithm only needs to know about cols and rows values and not index.
The allocation does work with cols and rows indeed, but will first run the code below where the information f is required:
static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) { struct sdw_master_prop *prop = &bus->prop; int frame_int, frame_freq; int r, c;
for (c = 0; c < SDW_FRAME_COLS; c++) { for (r = 0; r < SDW_FRAME_ROWS; r++) { if (sdw_rows[r] != prop->default_row || sdw_cols[c] != prop->default_col) continue;
frame_int = sdw_rows[r] * sdw_cols[c]; frame_freq = clk_freq / frame_int;
if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < bus->params.bandwidth) continue;
bus->params.row = sdw_rows[r]; bus->params.col = sdw_cols[c]; return 0; } }
return -EINVAL; }
as for the two helpers, they are used in both the allocation and the cadence code (to determine the initial frame shape from properties).
And other solutions for non-Intel platforms will also need this to convert from indices to frame shape.
So yes all of this is needed.
/**
- sdw_port_runtime: Runtime port parameters for Master or Slave
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a0476755a459..53f5e790fcd7 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -21,37 +21,39 @@
- The rows are arranged as per the array index value programmed
- in register. The index 15 has dummy value 0 in order to fill hole.
*/ -int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, +int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, 96, 100, 120, 128, 150, 160, 250, 0, 192, 200, 240, 256, 72, 144, 90, 180};
-int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
-static int sdw_find_col_index(int col) +int sdw_find_col_index(int col) { int i;
for (i = 0; i < SDW_FRAME_COLS; i++) {
if (cols[i] == col)
if (sdw_cols[i] == col) return i;
}
pr_warn("Requested column not found, selecting lowest column no: 2\n"); return 0; }
+EXPORT_SYMBOL(sdw_find_col_index);
-static int sdw_find_row_index(int row) +int sdw_find_row_index(int row) { int i;
for (i = 0; i < SDW_FRAME_ROWS; i++) {
if (rows[i] == row)
if (sdw_rows[i] == row) return i;
}
pr_warn("Requested row not found, selecting lowest row no: 48\n"); return 0; }
+EXPORT_SYMBOL(sdw_find_row_index);
static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave, -- 2.20.1
From: Bard liao yung-chuan.liao@linux.intel.com
Reported-by: kbuild test robot lkp@intel.com Signed-off-by: Bard liao yung-chuan.liao@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/linux/soundwire/sdw.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a49028e9d666..31d1e8acf25b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -4,6 +4,8 @@ #ifndef __SOUNDWIRE_H #define __SOUNDWIRE_H
+#include <linux/mod_devicetable.h> + struct sdw_bus; struct sdw_slave;
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream); + ret = do_bank_switch(stream); + if (ret < 0) { + dev_err(bus->dev, "Bank switch failed: %d\n", ret); + return ret; + } + + /* make sure alternate bank (previous current) is also disabled */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + /* Disable port(s) */ + ret = sdw_enable_disable_ports(m_rt, false); + if (ret < 0) { + dev_err(bus->dev, "Disable port(s) failed: %d\n", ret); + return ret; + } + } + + return 0; }
/**
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
- return do_bank_switch(stream);
ret = do_bank_switch(stream);
if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
}
/* make sure alternate bank (previous current) is also disabled */
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
}
}
return 0; }
/**
While not directly connected to this commit, I see that you do: link_for_each_entry(m_rt, &stream->master_list, stream_node)
quite often in /stream.c code. Helpful macro would prove useful.
On 7/26/19 5:14 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
- return do_bank_switch(stream); + ret = do_bank_switch(stream); + if (ret < 0) { + dev_err(bus->dev, "Bank switch failed: %d\n", ret); + return ret; + }
+ /* make sure alternate bank (previous current) is also disabled */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + /* Disable port(s) */ + ret = sdw_enable_disable_ports(m_rt, false); + if (ret < 0) { + dev_err(bus->dev, "Disable port(s) failed: %d\n", ret); + return ret; + } + }
+ return 0; } /**
While not directly connected to this commit, I see that you do: link_for_each_entry(m_rt, &stream->master_list, stream_node)
quite often in /stream.c code. Helpful macro would prove useful.
Yes, but the flip side is that people need to look at what the macro does to figure it out, while everyone knows what list_for_each_entry() means. Not sure about this one. And on top of this we'll probably have to rework this code to have a background copy of the current bank in the alternate bank so it'd rather leave it simple for now.
Unrelated to this specific patch, but I looked at _sdw_disable_stream() and I see this there (again, maybe my version is outdated already):
struct sdw_master_runtime *m_rt = NULL; struct sdw_bus *bus = NULL;
where both those initialisations are redundant. Moreover:
On Thu, Jul 25, 2019 at 06:40:15PM -0500, Pierre-Louis Bossart wrote:
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream);
- ret = do_bank_switch(stream);
- if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
- }
- /* make sure alternate bank (previous current) is also disabled */
- list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
"bus" is only used below, so at least take that line under "if (ret < 0)" or even just do "dev_err(m_rt->bus->dev,...)" everywhere in this function and remove the variable altogether...
Thanks Guennadi
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
}
- }
- return 0;
}
/**
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Unrelated to this specific patch, but I looked at _sdw_disable_stream() and I see this there (again, maybe my version is outdated already):
struct sdw_master_runtime *m_rt = NULL; struct sdw_bus *bus = NULL;
where both those initialisations are redundant. Moreover:
will look at this, thanks.
On Thu, Jul 25, 2019 at 06:40:15PM -0500, Pierre-Louis Bossart wrote:
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream);
- ret = do_bank_switch(stream);
- if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
- }
- /* make sure alternate bank (previous current) is also disabled */
- list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
"bus" is only used below, so at least take that line under "if (ret < 0)" or even just do "dev_err(m_rt->bus->dev,...)" everywhere in this function and remove the variable altogether...
will look at this, thanks Guennadi
Thanks Guennadi
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
On Thu, Jul 25, 2019 at 06:40:15PM -0500, Pierre-Louis Bossart wrote:
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
I didnt quite get why there will be two bank switches as part of disable flow which leads to enabling of stream?
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream);
- ret = do_bank_switch(stream);
- if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
- }
- /* make sure alternate bank (previous current) is also disabled */
- list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
}
- }
- return 0;
}
/**
2.20.1
--
On 8/5/19 4:56 AM, Sanyog Kale wrote:
On Thu, Jul 25, 2019 at 06:40:15PM -0500, Pierre-Louis Bossart wrote:
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
I didnt quite get why there will be two bank switches as part of disable flow which leads to enabling of stream?
You have two bank switches, one to stop streaming and on in de-prepare. It's symmetrical with the start sequence, where we do a bank switch to prepare and another to enable.
Let's assume we are using bank0 when streaming.
Before the first bank switch, the channel_enable is set to false in the alternate bank1. When the bank switch happens, bank1 become active and the streaming stops. But bank0 registers have not been modified so when we do the second bank switch in de-prepare we make bank0 active, and the ch_enable bits are still set so streaming will restart... When we stop streaming, we need to make sure the ch_enable bits are cleared in the two banks.
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream);
ret = do_bank_switch(stream);
if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
}
/* make sure alternate bank (previous current) is also disabled */
list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
}
}
return 0; }
/**
-- 2.20.1
On Mon, Aug 05, 2019 at 10:33:25AM -0500, Pierre-Louis Bossart wrote:
On 8/5/19 4:56 AM, Sanyog Kale wrote:
On Thu, Jul 25, 2019 at 06:40:15PM -0500, Pierre-Louis Bossart wrote:
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
I didnt quite get why there will be two bank switches as part of disable flow which leads to enabling of stream?
You have two bank switches, one to stop streaming and on in de-prepare. It's symmetrical with the start sequence, where we do a bank switch to prepare and another to enable.
Got it. I misunderstood it that two bank switches are performed as part of disable_stream.
Let's assume we are using bank0 when streaming.
Before the first bank switch, the channel_enable is set to false in the alternate bank1. When the bank switch happens, bank1 become active and the streaming stops. But bank0 registers have not been modified so when we do the second bank switch in de-prepare we make bank0 active, and the ch_enable bits are still set so streaming will restart... When we stop streaming, we need to make sure the ch_enable bits are cleared in the two banks.
This is clear. Even though the channels remains enabled, i believe there won't be any data pushed on lines as stream will be closed.
Regarding mirroring approach, I assume after bank switch we will take snapshot of active bank and program same in inactive bank.
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream);
- ret = do_bank_switch(stream);
- if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
- }
- /* make sure alternate bank (previous current) is also disabled */
- list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
}
- }
- return 0; } /**
-- 2.20.1
--
On 8/5/19 11:32 AM, Sanyog Kale wrote:
On Mon, Aug 05, 2019 at 10:33:25AM -0500, Pierre-Louis Bossart wrote:
On 8/5/19 4:56 AM, Sanyog Kale wrote:
On Thu, Jul 25, 2019 at 06:40:15PM -0500, Pierre-Louis Bossart wrote:
When we disable the stream and then call hw_free, two bank switches will be handled and as a result we re-enable the stream on hw_free.
I didnt quite get why there will be two bank switches as part of disable flow which leads to enabling of stream?
You have two bank switches, one to stop streaming and on in de-prepare. It's symmetrical with the start sequence, where we do a bank switch to prepare and another to enable.
Got it. I misunderstood it that two bank switches are performed as part of disable_stream.
Let's assume we are using bank0 when streaming.
Before the first bank switch, the channel_enable is set to false in the alternate bank1. When the bank switch happens, bank1 become active and the streaming stops. But bank0 registers have not been modified so when we do the second bank switch in de-prepare we make bank0 active, and the ch_enable bits are still set so streaming will restart... When we stop streaming, we need to make sure the ch_enable bits are cleared in the two banks.
This is clear. Even though the channels remains enabled, i believe there won't be any data pushed on lines as stream will be closed.
Actually the link remains active. We tested this by setting the PRBS data mode and the Slave device detects when we artificially inject errors.
There is however no data consumption on the DMA side of the Master IP since the DMA is indeed stopped.
Regarding mirroring approach, I assume after bank switch we will take snapshot of active bank and program same in inactive bank.
That should be the approach yes.
Make sure the stream is disabled on both banks.
TODO: we need to completely revisit all this and make sure we have a mirroring mechanism between current and alternate banks.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/stream.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 53f5e790fcd7..75b9ad1fb1a6 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1637,7 +1637,24 @@ static int _sdw_disable_stream(struct sdw_stream_runtime *stream) } }
- return do_bank_switch(stream);
- ret = do_bank_switch(stream);
- if (ret < 0) {
dev_err(bus->dev, "Bank switch failed: %d\n", ret);
return ret;
- }
- /* make sure alternate bank (previous current) is also disabled */
- list_for_each_entry(m_rt, &stream->master_list, stream_node) {
bus = m_rt->bus;
/* Disable port(s) */
ret = sdw_enable_disable_ports(m_rt, false);
if (ret < 0) {
dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
return ret;
}
- }
- return 0; } /**
-- 2.20.1
Remove hard-coding and use BIOS values. If they are wrong use default 48x2 frame shape.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 442f78c00f09..d84344e29f71 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -175,7 +175,6 @@ /* Driver defaults */
#define CDNS_DEFAULT_CLK_DIVIDER 0 -#define CDNS_DEFAULT_FRAME_SHAPE 0x30 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -954,6 +953,20 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, } EXPORT_SYMBOL(sdw_cdns_pdi_init);
+static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) +{ + u32 val; + int c; + int r; + + r = sdw_find_row_index(n_rows); + c = sdw_find_col_index(n_cols); + + val = (r << 3) | c; + + return val; +} + /** * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance @@ -977,7 +990,9 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
/* Set the default frame shape */ - cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE); + val = cdns_set_default_frame_shape(prop->default_row, + prop->default_col); + cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val);
/* Set SSP interval to default value */ cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) +{
- u32 val;
- int c;
- int r;
- r = sdw_find_row_index(n_rows);
- c = sdw_find_col_index(n_cols);
- val = (r << 3) | c;
- return val;
+}
- /**
- sdw_cdns_init() - Cadence initialization
- @cdns: Cadence instance
@@ -977,7 +990,9 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
/* Set the default frame shape */
- cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE);
val = cdns_set_default_frame_shape(prop->default_row,
prop->default_col);
cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val);
/* Set SSP interval to default value */ cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
Suggestion: declare "generic" _get_frame_frame(rows, cols) instead and let it do the bitwise operations for you. Pretty sure this won't be the only place in code where reg value for frame_shape needs to be prepared.
Said function could be as simple as: return (row << 3) | cols; +inline flag
i.e. could be even a macro..
Otherwise modify _set_default_frame_shape to simply: return (r << 3) | c
without involving additional local val variable (I don't really see the point for any locals there though).
On 7/26/19 5:20 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) +{ + u32 val; + int c; + int r;
+ r = sdw_find_row_index(n_rows); + c = sdw_find_col_index(n_cols);
+ val = (r << 3) | c;
+ return val; +}
/** * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance @@ -977,7 +990,9 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); /* Set the default frame shape */ - cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE); + val = cdns_set_default_frame_shape(prop->default_row, + prop->default_col); + cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val); /* Set SSP interval to default value */ cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
Suggestion: declare "generic" _get_frame_frame(rows, cols) instead and let it do the bitwise operations for you. Pretty sure this won't be the only place in code where reg value for frame_shape needs to be prepared.
Said function could be as simple as: return (row << 3) | cols; +inline flag
i.e. could be even a macro..
Otherwise modify _set_default_frame_shape to simply: return (r << 3) | c
without involving additional local val variable (I don't really see the point for any locals there though).
what this function does is take the standard-defined offsets for row and column and stores them in a Cadence-defined register. I think we can probably use a macro to remove the magic '3' value, but there are limits to what we can simplify. I should probably add comments so that people figure it out.
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Remove hard-coding and use BIOS values. If they are wrong use default
BIOS :) this is cadence, am sure this can be used outside BIOS :D
It would be better to say firmware (ACPI/DT)
48x2 frame shape.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 442f78c00f09..d84344e29f71 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -175,7 +175,6 @@ /* Driver defaults */
#define CDNS_DEFAULT_CLK_DIVIDER 0 -#define CDNS_DEFAULT_FRAME_SHAPE 0x30 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -954,6 +953,20 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, } EXPORT_SYMBOL(sdw_cdns_pdi_init);
+static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) +{
- u32 val;
- int c;
- int r;
This can be in single line!
- r = sdw_find_row_index(n_rows);
- c = sdw_find_col_index(n_cols);
- val = (r << 3) | c;
Magic 3?
On 8/2/19 12:10 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Remove hard-coding and use BIOS values. If they are wrong use default
BIOS :) this is cadence, am sure this can be used outside BIOS :D
It would be better to say firmware (ACPI/DT)
yes
48x2 frame shape.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 442f78c00f09..d84344e29f71 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -175,7 +175,6 @@ /* Driver defaults */
#define CDNS_DEFAULT_CLK_DIVIDER 0 -#define CDNS_DEFAULT_FRAME_SHAPE 0x30 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -954,6 +953,20 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, } EXPORT_SYMBOL(sdw_cdns_pdi_init);
+static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) +{
- u32 val;
- int c;
- int r;
This can be in single line!
one line per variable is what I prefer.
- r = sdw_find_row_index(n_rows);
- c = sdw_find_col_index(n_cols);
- val = (r << 3) | c;
Magic 3?
yes fixed already.
On Thu, Jul 25, 2019 at 06:40:16PM -0500, Pierre-Louis Bossart wrote:
Remove hard-coding and use BIOS values. If they are wrong use default 48x2 frame shape.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 442f78c00f09..d84344e29f71 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -175,7 +175,6 @@ /* Driver defaults */
#define CDNS_DEFAULT_CLK_DIVIDER 0 -#define CDNS_DEFAULT_FRAME_SHAPE 0x30 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -954,6 +953,20 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, } EXPORT_SYMBOL(sdw_cdns_pdi_init);
+static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) +{
- u32 val;
- int c;
- int r;
- r = sdw_find_row_index(n_rows);
- c = sdw_find_col_index(n_cols);
Now i get why you need above functions to be exported, please ignore my previous comment.
- val = (r << 3) | c;
- return val;
+}
/**
- sdw_cdns_init() - Cadence initialization
- @cdns: Cadence instance
@@ -977,7 +990,9 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
/* Set the default frame shape */
- cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE);
val = cdns_set_default_frame_shape(prop->default_row,
prop->default_col);
cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val);
/* Set SSP interval to default value */ cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL);
-- 2.20.1
--
The BIOS provides an Intel-specific property, let's use it to avoid hard-coded clock dividers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 26 ++++++++++++++++++++++---- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 ++ 3 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d84344e29f71..10ebcef2e84e 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -173,8 +173,6 @@ #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */ - -#define CDNS_DEFAULT_CLK_DIVIDER 0 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -973,7 +971,10 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) */ int sdw_cdns_init(struct sdw_cdns *cdns) { + struct sdw_bus *bus = &cdns->bus; + struct sdw_master_prop *prop = &bus->prop; u32 val; + int divider; int ret;
/* Exit clock stop */ @@ -985,9 +986,17 @@ int sdw_cdns_init(struct sdw_cdns *cdns) }
/* Set clock divider */ + divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0); - val |= CDNS_DEFAULT_CLK_DIVIDER; + val |= divider; cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); + cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val); + + pr_err("plb: mclk %d max_freq %d divider %d register %x\n", + prop->mclk_freq, + prop->max_clk_freq, + divider, + val);
/* Set the default frame shape */ val = cdns_set_default_frame_shape(prop->default_row, @@ -1035,6 +1044,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) { + struct sdw_master_prop *prop = &bus->prop; struct sdw_cdns *cdns = bus_to_cdns(bus); int mcp_clkctrl_off, mcp_clkctrl; int divider; @@ -1044,7 +1054,9 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) return -EINVAL; }
- divider = (params->max_dr_freq / params->curr_dr_freq) - 1; + divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR / + params->curr_dr_freq; + divider--; /* divider is 1/(N+1) */
if (params->next_bank) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; @@ -1055,6 +1067,12 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl |= divider; cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
+ pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", + prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR, + params->curr_dr_freq, + divider, + mcp_clkctrl); + return 0; } EXPORT_SYMBOL(cdns_bus_conf); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c718c9c67a37..796ac2bc8cea 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -917,11 +917,37 @@ static int intel_register_dai(struct sdw_intel *sdw) dais, num_dai); }
+static int sdw_master_read_intel_prop(struct sdw_bus *bus) +{ + struct sdw_master_prop *prop = &bus->prop; + struct fwnode_handle *link; + char name[32]; + int nval, i; + + /* Find master handle */ + snprintf(name, sizeof(name), + "mipi-sdw-link-%d-subproperties", bus->link_id); + + link = device_get_named_child_node(bus->dev, name); + if (!link) { + dev_err(bus->dev, "Master node %s not found\n", name); + return -EIO; + } + + fwnode_property_read_u32(link, + "intel-sdw-ip-clock", + &prop->mclk_freq); + return 0; +} + static int intel_prop_read(struct sdw_bus *bus) { /* Initialize with default handler to read all DisCo properties */ sdw_master_read_prop(bus);
+ /* read Intel-specific properties */ + sdw_master_read_intel_prop(bus); + return 0; }
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 31d1e8acf25b..b6acc436ac80 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -379,6 +379,7 @@ struct sdw_slave_prop { * @dynamic_frame: Dynamic frame shape supported * @err_threshold: Number of times that software may retry sending a single * command + * @mclk_freq: clock reference passed to SoundWire Master, in Hz. */ struct sdw_master_prop { u32 revision; @@ -393,6 +394,7 @@ struct sdw_master_prop { u32 default_col; bool dynamic_frame; u32 err_threshold; + u32 mclk_freq; };
int sdw_master_read_prop(struct sdw_bus *bus);
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
The BIOS provides an Intel-specific property, let's use it to avoid hard-coded clock dividers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 26 ++++++++++++++++++++++---- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 ++
ah, intel patch touching bunch of things!
3 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d84344e29f71..10ebcef2e84e 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -173,8 +173,6 @@ #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */
-#define CDNS_DEFAULT_CLK_DIVIDER 0 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -973,7 +971,10 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) */ int sdw_cdns_init(struct sdw_cdns *cdns) {
struct sdw_bus *bus = &cdns->bus;
struct sdw_master_prop *prop = &bus->prop; u32 val;
int divider; int ret;
/* Exit clock stop */
@@ -985,9 +986,17 @@ int sdw_cdns_init(struct sdw_cdns *cdns) }
/* Set clock divider */
- divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
- val |= CDNS_DEFAULT_CLK_DIVIDER;
- val |= divider; cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
- pr_err("plb: mclk %d max_freq %d divider %d register %x\n",
prop->mclk_freq,
prop->max_clk_freq,
divider,
val);
I guess you forgot to remove this!
/* Set the default frame shape */ val = cdns_set_default_frame_shape(prop->default_row, @@ -1035,6 +1044,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) {
- struct sdw_master_prop *prop = &bus->prop; struct sdw_cdns *cdns = bus_to_cdns(bus); int mcp_clkctrl_off, mcp_clkctrl; int divider;
@@ -1044,7 +1054,9 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) return -EINVAL; }
- divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
params->curr_dr_freq;
divider--; /* divider is 1/(N+1) */
if (params->next_bank) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
@@ -1055,6 +1067,12 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl |= divider; cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
- pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n",
prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
params->curr_dr_freq,
divider,
mcp_clkctrl);
here too!
- return 0;
} EXPORT_SYMBOL(cdns_bus_conf); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c718c9c67a37..796ac2bc8cea 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -917,11 +917,37 @@ static int intel_register_dai(struct sdw_intel *sdw) dais, num_dai); }
+static int sdw_master_read_intel_prop(struct sdw_bus *bus) +{
- struct sdw_master_prop *prop = &bus->prop;
- struct fwnode_handle *link;
- char name[32];
- int nval, i;
- /* Find master handle */
- snprintf(name, sizeof(name),
"mipi-sdw-link-%d-subproperties", bus->link_id);
- link = device_get_named_child_node(bus->dev, name);
- if (!link) {
dev_err(bus->dev, "Master node %s not found\n", name);
return -EIO;
- }
- fwnode_property_read_u32(link,
"intel-sdw-ip-clock",
&prop->mclk_freq);
- return 0;
+}
static int intel_prop_read(struct sdw_bus *bus) { /* Initialize with default handler to read all DisCo properties */ sdw_master_read_prop(bus);
- /* read Intel-specific properties */
- sdw_master_read_intel_prop(bus);
- return 0;
}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 31d1e8acf25b..b6acc436ac80 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -379,6 +379,7 @@ struct sdw_slave_prop {
- @dynamic_frame: Dynamic frame shape supported
- @err_threshold: Number of times that software may retry sending a single
- command
*/
- @mclk_freq: clock reference passed to SoundWire Master, in Hz.
struct sdw_master_prop { u32 revision; @@ -393,6 +394,7 @@ struct sdw_master_prop { u32 default_col; bool dynamic_frame; u32 err_threshold;
- u32 mclk_freq;
Other than debug artifacts this looks sane, but can you split up the cadence and intel parts into different patches please
On 8/2/19 12:17 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
The BIOS provides an Intel-specific property, let's use it to avoid hard-coded clock dividers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 26 ++++++++++++++++++++++---- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 ++
ah, intel patch touching bunch of things!
not really, it's more removing Intel-specific configs in the Cadence parts.
3 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d84344e29f71..10ebcef2e84e 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -173,8 +173,6 @@ #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */
-#define CDNS_DEFAULT_CLK_DIVIDER 0 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -973,7 +971,10 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) */ int sdw_cdns_init(struct sdw_cdns *cdns) {
struct sdw_bus *bus = &cdns->bus;
struct sdw_master_prop *prop = &bus->prop; u32 val;
int divider; int ret;
/* Exit clock stop */
@@ -985,9 +986,17 @@ int sdw_cdns_init(struct sdw_cdns *cdns) }
/* Set clock divider */
- divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
- val |= CDNS_DEFAULT_CLK_DIVIDER;
- val |= divider; cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
- pr_err("plb: mclk %d max_freq %d divider %d register %x\n",
prop->mclk_freq,
prop->max_clk_freq,
divider,
val);
I guess you forgot to remove this!
yes, fixed already
/* Set the default frame shape */ val = cdns_set_default_frame_shape(prop->default_row, @@ -1035,6 +1044,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) {
- struct sdw_master_prop *prop = &bus->prop; struct sdw_cdns *cdns = bus_to_cdns(bus); int mcp_clkctrl_off, mcp_clkctrl; int divider;
@@ -1044,7 +1054,9 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) return -EINVAL; }
- divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
params->curr_dr_freq;
divider--; /* divider is 1/(N+1) */
if (params->next_bank) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
@@ -1055,6 +1067,12 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl |= divider; cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
- pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n",
prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
params->curr_dr_freq,
divider,
mcp_clkctrl);
here too!
yep
- return 0; } EXPORT_SYMBOL(cdns_bus_conf);
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c718c9c67a37..796ac2bc8cea 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -917,11 +917,37 @@ static int intel_register_dai(struct sdw_intel *sdw) dais, num_dai); }
+static int sdw_master_read_intel_prop(struct sdw_bus *bus) +{
- struct sdw_master_prop *prop = &bus->prop;
- struct fwnode_handle *link;
- char name[32];
- int nval, i;
- /* Find master handle */
- snprintf(name, sizeof(name),
"mipi-sdw-link-%d-subproperties", bus->link_id);
- link = device_get_named_child_node(bus->dev, name);
- if (!link) {
dev_err(bus->dev, "Master node %s not found\n", name);
return -EIO;
- }
- fwnode_property_read_u32(link,
"intel-sdw-ip-clock",
&prop->mclk_freq);
- return 0;
+}
static int intel_prop_read(struct sdw_bus *bus) { /* Initialize with default handler to read all DisCo properties */ sdw_master_read_prop(bus);
/* read Intel-specific properties */
sdw_master_read_intel_prop(bus);
return 0; }
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 31d1e8acf25b..b6acc436ac80 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -379,6 +379,7 @@ struct sdw_slave_prop {
- @dynamic_frame: Dynamic frame shape supported
- @err_threshold: Number of times that software may retry sending a single
- command
*/ struct sdw_master_prop { u32 revision;
- @mclk_freq: clock reference passed to SoundWire Master, in Hz.
@@ -393,6 +394,7 @@ struct sdw_master_prop { u32 default_col; bool dynamic_frame; u32 err_threshold;
- u32 mclk_freq;
Other than debug artifacts this looks sane, but can you split up the cadence and intel parts into different patches please
ok, i'll split in 3. prototype, intel, cadence.
On Thu, Jul 25, 2019 at 06:40:17PM -0500, Pierre-Louis Bossart wrote:
The BIOS provides an Intel-specific property, let's use it to avoid hard-coded clock dividers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 26 ++++++++++++++++++++++---- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 ++ 3 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d84344e29f71..10ebcef2e84e 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -173,8 +173,6 @@ #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */
-#define CDNS_DEFAULT_CLK_DIVIDER 0 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -973,7 +971,10 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) */ int sdw_cdns_init(struct sdw_cdns *cdns) {
struct sdw_bus *bus = &cdns->bus;
struct sdw_master_prop *prop = &bus->prop; u32 val;
int divider; int ret;
/* Exit clock stop */
@@ -985,9 +986,17 @@ int sdw_cdns_init(struct sdw_cdns *cdns) }
/* Set clock divider */
- divider = (prop->mclk_freq / prop->max_clk_freq) - 1;
Do you expect mclk_freq and max_clk_freq to be same?
val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
- val |= CDNS_DEFAULT_CLK_DIVIDER;
- val |= divider; cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
- pr_err("plb: mclk %d max_freq %d divider %d register %x\n",
prop->mclk_freq,
prop->max_clk_freq,
divider,
val);
This can be removed.
/* Set the default frame shape */ val = cdns_set_default_frame_shape(prop->default_row, @@ -1035,6 +1044,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) {
- struct sdw_master_prop *prop = &bus->prop; struct sdw_cdns *cdns = bus_to_cdns(bus); int mcp_clkctrl_off, mcp_clkctrl; int divider;
@@ -1044,7 +1054,9 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) return -EINVAL; }
- divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
- divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
What is the reason for not using max_dr_freq? Its precomputed as prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR;
params->curr_dr_freq;
divider--; /* divider is 1/(N+1) */
if (params->next_bank) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
@@ -1055,6 +1067,12 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl |= divider; cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
- pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n",
prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
params->curr_dr_freq,
divider,
mcp_clkctrl);
- return 0;
} EXPORT_SYMBOL(cdns_bus_conf); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c718c9c67a37..796ac2bc8cea 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -917,11 +917,37 @@ static int intel_register_dai(struct sdw_intel *sdw) dais, num_dai); }
+static int sdw_master_read_intel_prop(struct sdw_bus *bus) +{
- struct sdw_master_prop *prop = &bus->prop;
- struct fwnode_handle *link;
- char name[32];
- int nval, i;
- /* Find master handle */
- snprintf(name, sizeof(name),
"mipi-sdw-link-%d-subproperties", bus->link_id);
- link = device_get_named_child_node(bus->dev, name);
- if (!link) {
dev_err(bus->dev, "Master node %s not found\n", name);
return -EIO;
- }
- fwnode_property_read_u32(link,
"intel-sdw-ip-clock",
&prop->mclk_freq);
- return 0;
+}
static int intel_prop_read(struct sdw_bus *bus) { /* Initialize with default handler to read all DisCo properties */ sdw_master_read_prop(bus);
- /* read Intel-specific properties */
- sdw_master_read_intel_prop(bus);
- return 0;
}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 31d1e8acf25b..b6acc436ac80 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -379,6 +379,7 @@ struct sdw_slave_prop {
- @dynamic_frame: Dynamic frame shape supported
- @err_threshold: Number of times that software may retry sending a single
- command
*/
- @mclk_freq: clock reference passed to SoundWire Master, in Hz.
struct sdw_master_prop { u32 revision; @@ -393,6 +394,7 @@ struct sdw_master_prop { u32 default_col; bool dynamic_frame; u32 err_threshold;
- u32 mclk_freq;
};
int sdw_master_read_prop(struct sdw_bus *bus);
2.20.1
--
On 8/5/19 5:28 AM, Sanyog Kale wrote:
On Thu, Jul 25, 2019 at 06:40:17PM -0500, Pierre-Louis Bossart wrote:
The BIOS provides an Intel-specific property, let's use it to avoid hard-coded clock dividers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 26 ++++++++++++++++++++++---- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 ++ 3 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d84344e29f71..10ebcef2e84e 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -173,8 +173,6 @@ #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0)
/* Driver defaults */
-#define CDNS_DEFAULT_CLK_DIVIDER 0 #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000
@@ -973,7 +971,10 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) */ int sdw_cdns_init(struct sdw_cdns *cdns) {
struct sdw_bus *bus = &cdns->bus;
struct sdw_master_prop *prop = &bus->prop; u32 val;
int divider; int ret;
/* Exit clock stop */
@@ -985,9 +986,17 @@ int sdw_cdns_init(struct sdw_cdns *cdns) }
/* Set clock divider */
- divider = (prop->mclk_freq / prop->max_clk_freq) - 1;
Do you expect mclk_freq and max_clk_freq to be same?
Nope. For Icelake the MCLK is 38.4 MHz and is, but the max_clk needs to be 9.6 max (can't be higher per the SoundWire spec).
The max_clk_freq may even be lower thank 9.6 MHz due to specific topologies where the higher frequencies are problematic if the trace lengths are too long.
For CNL/CML the MCLK is 24 MHz but ironically we run the bus at 12 MHz, so the divider is smaller.
val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
- val |= CDNS_DEFAULT_CLK_DIVIDER;
- val |= divider; cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
- pr_err("plb: mclk %d max_freq %d divider %d register %x\n",
prop->mclk_freq,
prop->max_clk_freq,
divider,
val);
This can be removed.
yes, done already.
/* Set the default frame shape */ val = cdns_set_default_frame_shape(prop->default_row, @@ -1035,6 +1044,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) {
- struct sdw_master_prop *prop = &bus->prop; struct sdw_cdns *cdns = bus_to_cdns(bus); int mcp_clkctrl_off, mcp_clkctrl; int divider;
@@ -1044,7 +1054,9 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) return -EINVAL; }
- divider = (params->max_dr_freq / params->curr_dr_freq) - 1;
- divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR /
What is the reason for not using max_dr_freq? Its precomputed as prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR;
no, as explained above the divider needs to start from the clock provided to the IP, which is different from the max frequency clock the bus operates at. the MCLK is a fixed value for all platforms using the same SOC/PCH, the max_clk is platform-dependent and its value is provided by the firmware (BIOS/DT).
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
Add the mask definition and use cdns_updatel to update divider
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 10ebcef2e84e..18c6ac026e85 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -57,6 +57,7 @@ #define CDNS_MCP_SSP_CTRL1 0x28 #define CDNS_MCP_CLK_CTRL0 0x30 #define CDNS_MCP_CLK_CTRL1 0x38 +#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0)
#define CDNS_MCP_STAT 0x40
@@ -988,9 +989,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0); - val |= divider; - cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); - cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val); + + cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0, + CDNS_MCP_CLK_MCLKD_MASK, divider); + cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1, + CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk %d max_freq %d divider %d register %x\n", prop->mclk_freq, @@ -1064,8 +1067,7 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); - mcp_clkctrl |= divider; - cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); + cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
On 7/26/2019 7:40 AM, Pierre-Louis Bossart wrote:
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
Add the mask definition and use cdns_updatel to update divider
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 10ebcef2e84e..18c6ac026e85 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -57,6 +57,7 @@ #define CDNS_MCP_SSP_CTRL1 0x28 #define CDNS_MCP_CLK_CTRL0 0x30 #define CDNS_MCP_CLK_CTRL1 0x38 +#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0)
#define CDNS_MCP_STAT 0x40
@@ -988,9 +989,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
Do we still need to read cdns_readl(cdns, CDNS_MCP_CLK_CTRL0)
after this change?
- val |= divider;
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
CDNS_MCP_CLK_MCLKD_MASK, divider);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk %d max_freq %d divider %d register %x\n", prop->mclk_freq,
@@ -1064,8 +1067,7 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
Same here.
- mcp_clkctrl |= divider;
- cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
在 7/26/2019 1:19 PM, Bard liao 写道:
On 7/26/2019 7:40 AM, Pierre-Louis Bossart wrote:
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
Add the mask definition and use cdns_updatel to update divider
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart
pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 10ebcef2e84e..18c6ac026e85 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -57,6 +57,7 @@ #define CDNS_MCP_SSP_CTRL1 0x28 #define CDNS_MCP_CLK_CTRL0 0x30 #define CDNS_MCP_CLK_CTRL1 0x38 +#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0) #define CDNS_MCP_STAT 0x40 @@ -988,9 +989,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
Do we still need to read cdns_readl(cdns, CDNS_MCP_CLK_CTRL0)
after this change?
The val is used to print debug message, and my opinion is to change the log
from pr_err("plb: ........") to dev_dbg(bus->dev, "........") to follow the dev_dbg
usage in this file.
- val |= divider; - cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); - cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
+ cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0, + CDNS_MCP_CLK_MCLKD_MASK, divider); + cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1, + CDNS_MCP_CLK_MCLKD_MASK, divider); pr_err("plb: mclk %d max_freq %d divider %d register %x\n", prop->mclk_freq, @@ -1064,8 +1067,7 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
Same here.
Also refine the debug log.
- mcp_clkctrl |= divider; - cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); + cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider); pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
On 7/26/19 12:19 AM, Bard liao wrote:
On 7/26/2019 7:40 AM, Pierre-Louis Bossart wrote:
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
Add the mask definition and use cdns_updatel to update divider
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart
pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 10ebcef2e84e..18c6ac026e85 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -57,6 +57,7 @@ #define CDNS_MCP_SSP_CTRL1 0x28 #define CDNS_MCP_CLK_CTRL0 0x30 #define CDNS_MCP_CLK_CTRL1 0x38 +#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0) #define CDNS_MCP_STAT 0x40 @@ -988,9 +989,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
Do we still need to read cdns_readl(cdns, CDNS_MCP_CLK_CTRL0)
after this change?
no I don't think we do indeed.
- val |= divider; - cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); - cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
+ cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0, + CDNS_MCP_CLK_MCLKD_MASK, divider); + cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1, + CDNS_MCP_CLK_MCLKD_MASK, divider); pr_err("plb: mclk %d max_freq %d divider %d register %x\n", prop->mclk_freq,
and this log needs to go away or done in a better way, I missed this.
@@ -1064,8 +1067,7 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
Same here.
yep.
- mcp_clkctrl |= divider; - cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); + cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider); pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
This looked fine but fails to apply, feel free to send as a fix
On 8/2/19 12:19 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
This looked fine but fails to apply, feel free to send as a fix
likely because it's on top of patch 25
On Thu, Jul 25, 2019 at 06:40:18PM -0500, Pierre-Louis Bossart wrote:
From: Rander Wang rander.wang@linux.intel.com
The existing code uses an OR operation which would mix the original divider setting with the new one, resulting in an invalid configuration that can make codecs hang.
Add the mask definition and use cdns_updatel to update divider
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 10ebcef2e84e..18c6ac026e85 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -57,6 +57,7 @@ #define CDNS_MCP_SSP_CTRL1 0x28 #define CDNS_MCP_CLK_CTRL0 0x30 #define CDNS_MCP_CLK_CTRL1 0x38 +#define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0)
#define CDNS_MCP_STAT 0x40
@@ -988,9 +989,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
reg read of CLK_CTRL0 can be removed.
- val |= divider;
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
CDNS_MCP_CLK_MCLKD_MASK, divider);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk %d max_freq %d divider %d register %x\n", prop->mclk_freq,
@@ -1064,8 +1067,7 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
same as above.
- mcp_clkctrl |= divider;
- cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
-- 2.20.1
--
@@ -988,9 +989,11 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set clock divider */ divider = (prop->mclk_freq / prop->max_clk_freq) - 1; val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0);
reg read of CLK_CTRL0 can be removed.
yes for both comments. Thanks for the review Sanyog, appreciate it.
- val |= divider;
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val);
- cdns_writel(cdns, CDNS_MCP_CLK_CTRL1, val);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0,
CDNS_MCP_CLK_MCLKD_MASK, divider);
cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1,
CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk %d max_freq %d divider %d register %x\n", prop->mclk_freq,
@@ -1064,8 +1067,7 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
same as above.
- mcp_clkctrl |= divider;
- cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider);
pr_err("plb: mclk * 2 %d curr_dr_freq %d divider %d register %x\n", prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR,
-- 2.20.1
This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus.
This implementation is optimal for Intel platforms. Developers can also implement their own .compute_params() callback for specific resource management algorithm.
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead.
FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug + group->rates = krealloc(group->rates,
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/Makefile | 2 +- drivers/soundwire/algo_dynamic_allocation.c | 403 ++++++++++++++++++++ drivers/soundwire/bus.c | 3 + drivers/soundwire/bus.h | 46 ++- drivers/soundwire/stream.c | 20 + include/linux/soundwire/sdw.h | 5 + 6 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 drivers/soundwire/algo_dynamic_allocation.c
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 88990cac48a7..f59a9d4a28fd 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -5,7 +5,7 @@
#Bus Objs soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o \ - debugfs.o + debugfs.o algo_dynamic_allocation.o
obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
diff --git a/drivers/soundwire/algo_dynamic_allocation.c b/drivers/soundwire/algo_dynamic_allocation.c new file mode 100644 index 000000000000..89edb39162b8 --- /dev/null +++ b/drivers/soundwire/algo_dynamic_allocation.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2015-18 Intel Corporation. + +/* + * Bandwidth management algorithm based on 2^n gears + * + */ + +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include "bus.h" + +#define SDW_STRM_RATE_GROUPING 1 + +struct sdw_group_params { + unsigned int rate; + int full_bw; + int payload_bw; + int hwidth; +}; + +struct sdw_group { + unsigned int count; + unsigned int max_size; + unsigned int *rates; +}; + +struct sdw_transport_data { + int hstart; + int hstop; + int block_offset; + int sub_block_offset; +}; + +static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt, + struct sdw_transport_data *t_data) +{ + struct sdw_slave_runtime *s_rt = NULL; + struct sdw_port_runtime *p_rt; + int port_bo, sample_int; + unsigned int rate, bps, ch = 0; + + port_bo = t_data->block_offset; + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + sample_int = (m_rt->bus->params.curr_dr_freq / rate); + + list_for_each_entry(p_rt, &s_rt->port_list, port_node) { + ch = sdw_ch_mask_to_ch(p_rt->ch_mask); + + sdw_fill_xport_params(&p_rt->transport_params, + p_rt->num, true, + SDW_BLK_GRP_CNT_1, + sample_int, port_bo, port_bo >> 8, + t_data->hstart, + t_data->hstop, + (SDW_BLK_GRP_CNT_1 * ch), 0x0); + + sdw_fill_port_params(&p_rt->port_params, + p_rt->num, bps, + SDW_PORT_FLOW_MODE_ISOCH, + SDW_PORT_DATA_MODE_NORMAL); + + port_bo += bps * ch; + } + } +} + +static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt, + struct sdw_group_params *params, + int port_bo, int hstop) +{ + struct sdw_transport_data t_data = {0}; + struct sdw_port_runtime *p_rt; + struct sdw_bus *bus = m_rt->bus; + int sample_int, hstart = 0; + unsigned int rate, bps, ch, no_ch; + + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = m_rt->ch_count; + sample_int = (bus->params.curr_dr_freq / rate); + + if (rate != params->rate) + return; + + t_data.hstop = hstop; + hstart = hstop - params->hwidth + 1; + t_data.hstart = hstart; + + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { + no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask); + + sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, + true, SDW_BLK_GRP_CNT_1, sample_int, + port_bo, port_bo >> 8, hstart, hstop, + (SDW_BLK_GRP_CNT_1 * no_ch), 0x0); + + sdw_fill_port_params(&p_rt->port_params, + p_rt->num, bps, + SDW_PORT_FLOW_MODE_ISOCH, + SDW_PORT_DATA_MODE_NORMAL); + + /* Check for first entry */ + if (!(p_rt == list_first_entry(&m_rt->port_list, + struct sdw_port_runtime, + port_node))) { + port_bo += bps * ch; + continue; + } + + t_data.hstart = hstart; + t_data.hstop = hstop; + t_data.block_offset = port_bo; + t_data.sub_block_offset = 0; + port_bo += bps * ch; + } + + sdw_compute_slave_ports(m_rt, &t_data); +} + +static void _sdw_compute_port_params(struct sdw_bus *bus, + struct sdw_group_params *params, int count) +{ + struct sdw_master_runtime *m_rt = NULL; + int hstop = bus->params.col - 1; + int block_offset, port_bo, i; + + /* Run loop for all groups to compute transport parameters */ + for (i = 0; i < count; i++) { + port_bo = 1; + block_offset = 1; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + sdw_compute_master_ports(m_rt, ¶ms[i], + port_bo, hstop); + + block_offset += m_rt->ch_count * + m_rt->stream->params.bps; + port_bo = block_offset; + } + + hstop = hstop - params[i].hwidth; + } +} + +static int sdw_compute_group_params(struct sdw_bus *bus, + struct sdw_group_params *params, + int *rates, int count) +{ + struct sdw_master_runtime *m_rt = NULL; + int sel_col = bus->params.col; + unsigned int rate, bps, ch; + int i, column_needed = 0; + + /* Calculate bandwidth per group */ + for (i = 0; i < count; i++) { + params[i].rate = rates[i]; + params[i].full_bw = bus->params.curr_dr_freq / params[i].rate; + } + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + rate = m_rt->stream->params.rate; + bps = m_rt->stream->params.bps; + ch = m_rt->ch_count; + + for (i = 0; i < count; i++) { + if (rate == params[i].rate) + params[i].payload_bw += bps * ch; + } + } + + for (i = 0; i < count; i++) { + params[i].hwidth = (sel_col * + params[i].payload_bw + params[i].full_bw - 1) / + params[i].full_bw; + + column_needed += params[i].hwidth; + } + + if (column_needed > sel_col - 1) + return -EINVAL; + + return 0; +} + +static int sdw_add_element_group_count(struct sdw_group *group, + unsigned int rate) +{ + int num = group->count; + int i; + + for (i = 0; i <= num; i++) { + if (rate == group->rates[i]) + break; + + if (i != num) + continue; + + if (group->count >= group->max_size) { + group->max_size += 1; + group->rates = krealloc(group->rates, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!group->rates) + return -ENOMEM; + } + + group->rates[group->count++] = rate; + } + + return 0; +} + +static int sdw_get_group_count(struct sdw_bus *bus, + struct sdw_group *group) +{ + struct sdw_master_runtime *m_rt; + unsigned int rate; + int ret = 0; + + group->count = 0; + group->max_size = SDW_STRM_RATE_GROUPING; + group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL); + if (!group->rates) + return -ENOMEM; + + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + rate = m_rt->stream->params.rate; + if (m_rt == list_first_entry(&bus->m_rt_list, + struct sdw_master_runtime, + bus_node)) { + group->rates[group->count++] = rate; + + } else { + ret = sdw_add_element_group_count(group, rate); + if (ret < 0) + return ret; + } + } + + return ret; +} + +/** + * sdw_compute_port_params: Compute transport and port parameters + * + * @bus: SDW Bus instance + */ +static int sdw_compute_port_params(struct sdw_bus *bus) +{ + struct sdw_group_params *params = NULL; + struct sdw_group group; + int ret; + + ret = sdw_get_group_count(bus, &group); + if (ret < 0) + goto out; + + if (group.count == 0) + goto out; + + params = kcalloc(group.count, sizeof(*params), GFP_KERNEL); + if (!params) { + ret = -ENOMEM; + goto out; + } + + /* Compute transport parameters for grouped streams */ + ret = sdw_compute_group_params(bus, params, + &group.rates[0], group.count); + if (ret < 0) + goto out; + + _sdw_compute_port_params(bus, params, group.count); + +out: + kfree(params); + kfree(group.rates); + + return ret; +} + +static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) +{ + struct sdw_master_prop *prop = &bus->prop; + int frame_int, frame_freq; + int r, c; + + for (c = 0; c < SDW_FRAME_COLS; c++) { + for (r = 0; r < SDW_FRAME_ROWS; r++) { + if (sdw_rows[r] != prop->default_row || + sdw_cols[c] != prop->default_col) + continue; + + frame_int = sdw_rows[r] * sdw_cols[c]; + frame_freq = clk_freq / frame_int; + + if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) < + bus->params.bandwidth) + continue; + + bus->params.row = sdw_rows[r]; + bus->params.col = sdw_cols[c]; + return 0; + } + } + + return -EINVAL; +} + +/** + * sdw_compute_bus_params: Compute bus parameters + * + * @bus: SDW Bus instance + */ +static int sdw_compute_bus_params(struct sdw_bus *bus) +{ + unsigned int max_dr_freq, curr_dr_freq = 0; + struct sdw_master_prop *mstr_prop = NULL; + int i, clk_values, ret; + bool is_gear = false; + u32 *clk_buf; + + mstr_prop = &bus->prop; + if (!mstr_prop) + return -EINVAL; + + if (mstr_prop->num_clk_gears) { + clk_values = mstr_prop->num_clk_gears; + clk_buf = mstr_prop->clk_gears; + is_gear = true; + } else if (mstr_prop->num_clk_freq) { + clk_values = mstr_prop->num_clk_freq; + clk_buf = mstr_prop->clk_freq; + } else { + clk_values = 1; + clk_buf = NULL; + } + + max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; + + for (i = 0; i < clk_values; i++) { + if (!clk_buf) + curr_dr_freq = max_dr_freq; + else + curr_dr_freq = (is_gear) ? + (max_dr_freq >> clk_buf[i]) : + clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; + + if (curr_dr_freq <= bus->params.bandwidth) + continue; + + break; + + /* + * TODO: Check all the Slave(s) port(s) audio modes and find + * whether given clock rate is supported with glitchless + * transition. + */ + } + + if (i == clk_values) + return -EINVAL; + + ret = sdw_select_row_col(bus, curr_dr_freq); + if (ret < 0) + return -EINVAL; + + bus->params.curr_dr_freq = curr_dr_freq; + return 0; +} + +/** + * sdw_compute_params: Compute bus, transport and port parameters + * + * @bus: SDW Bus instance + */ +int sdw_compute_params(struct sdw_bus *bus) +{ + int ret; + + /* Computes clock frequency, frame shape and frame frequency */ + ret = sdw_compute_bus_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute bus params failed: %d", ret); + return ret; + } + + /* Compute transport and port params */ + ret = sdw_compute_port_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Compute transport params failed: %d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sdw_compute_params); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 2354675ef104..76a180578712 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -51,6 +51,9 @@ int sdw_add_bus_master(struct sdw_bus *bus)
bus->debugfs = sdw_bus_debugfs_init(bus);
+ if (!bus->compute_params) + bus->compute_params = &sdw_compute_params; + /* * Device numbers in SoundWire are 0 through 15. Enumeration device * number (0), Broadcast device number (15), Group numbers (12 and diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index c57c9c23f6ca..fdb7ce034fdf 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -72,6 +72,7 @@ struct sdw_msg { };
#define SDW_DOUBLE_RATE_FACTOR 2 +#define SDW_STRM_RATE_GROUPING 1
extern int sdw_rows[SDW_FRAME_ROWS]; extern int sdw_cols[SDW_FRAME_COLS]; @@ -157,9 +158,50 @@ int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf);
+/* Retrieve and return channel count from channel mask */ +static inline int sdw_ch_mask_to_ch(int ch_mask) +{ + int c = 0; + + for (c = 0; ch_mask; ch_mask >>= 1) + c += ch_mask & 1; + + return c; +} + +/* Fill transport parameter data structure */ +static inline void sdw_fill_xport_params(struct sdw_transport_params *params, + int port_num, bool grp_ctrl_valid, + int grp_ctrl, int sample_int, + int off1, int off2, + int hstart, int hstop, + int pack_mode, int lane_ctrl) +{ + params->port_num = port_num; + params->blk_grp_ctrl_valid = grp_ctrl_valid; + params->blk_grp_ctrl = grp_ctrl; + params->sample_interval = sample_int; + params->offset1 = off1; + params->offset2 = off2; + params->hstart = hstart; + params->hstop = hstop; + params->blk_pkg_mode = pack_mode; + params->lane_ctrl = lane_ctrl; +} + +/* Fill port parameter data structure */ +static inline void sdw_fill_port_params(struct sdw_port_params *params, + int port_num, int bps, + int flow_mode, int data_mode) +{ + params->num = port_num; + params->bps = bps; + params->flow_mode = flow_mode; + params->data_mode = data_mode; +} + /* Read-Modify-Write Slave register */ -static inline int -sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) +static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) { int tmp;
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 75b9ad1fb1a6..add7c121d084 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1485,6 +1485,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) bus->params.bandwidth += m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps;
+ /* Compute params */ + if (bus->compute_params) { + ret = bus->compute_params(bus); + if (ret < 0) { + dev_err(bus->dev, + "Compute params failed: %d", ret); + return ret; + } + } + /* Program params */ ret = sdw_program_params(bus); if (ret < 0) { @@ -1704,6 +1714,16 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) bus->params.bandwidth -= m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps;
+ /* Compute params */ + if (bus->compute_params) { + ret = bus->compute_params(bus); + if (ret < 0) { + dev_err(bus->dev, + "Compute params failed: %d", ret); + return ret; + } + } + /* Program params */ ret = sdw_program_params(bus); if (ret < 0) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index b6acc436ac80..c7dfc824be80 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -730,6 +730,7 @@ struct sdw_master_ops { * Bit set implies used number, bit clear implies unused number. * @bus_lock: bus lock * @msg_lock: message lock + * @compute_params: points to Bus resource management implementation * @ops: Master callback ops * @port_ops: Master port callback ops * @params: Current bus parameters @@ -752,6 +753,7 @@ struct sdw_bus { DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); struct mutex bus_lock; struct mutex msg_lock; + int (*compute_params)(struct sdw_bus *bus); const struct sdw_master_ops *ops; const struct sdw_master_port_ops *port_ops; struct sdw_bus_params params; @@ -852,6 +854,9 @@ struct sdw_stream_runtime {
struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name); void sdw_release_stream(struct sdw_stream_runtime *stream); + +int sdw_compute_params(struct sdw_bus *bus); + int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, struct sdw_port_config *port_config,
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus.
This implementation is optimal for Intel platforms. Developers can also implement their own .compute_params() callback for specific resource management algorithm.
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead.
FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug
group->rates = krealloc(group->rates,
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Could you specify the requirements and limitations for this algorithm? Last year I written calc for Linux based on Windows (please don't burn me here) equivalent though said requirements/ limitiations might have changed and nothing is valid any longer.
I remember that some parts of specification overcomplicated the calculator and due to actual, realtime usecases it could be greatly simplified (that's why I mention that my work is probably no longer valid). However, these details would help me in reviewing your implementation and providing suggestions.
And yes, "Frame shape calculator" probably suits this better. Though this might be just a preference thingy : )
On 7/26/19 6:07 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus.
This implementation is optimal for Intel platforms. Developers can also implement their own .compute_params() callback for specific resource management algorithm.
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead.
FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug + group->rates = krealloc(group->rates,
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Could you specify the requirements and limitations for this algorithm? Last year I written calc for Linux based on Windows (please don't burn me here) equivalent though said requirements/ limitiations might have changed and nothing is valid any longer.
The frame shape typically only changes by doubling the number of columns, and the priority is given to PDM streams which use columns. It's hard to document this on a public mailing list, it'd require making references to a spec that's only available to MIPI members.
I remember that some parts of specification overcomplicated the calculator and due to actual, realtime usecases it could be greatly simplified (that's why I mention that my work is probably no longer valid). However, these details would help me in reviewing your implementation and providing suggestions.
To the best of my knowledge, the algorithm follows a script that is used for both Windows and Linux. The code was initially written by Vinod and team, and I am not aware of simplifications. There a simplifications that are possible, e.g. we don't support PDM for now and the notion of grouping is not needed, but we have to carefully balance 'optimization' with 'introducing bugs'. if this algorithm craps out then the entire bus operation is in the weeds.
If we really want people to experiment and get a feel for the performance of the algorithm, we should really provide a UI where the stream parameters can be entered and visually check what the algorithm is doing. I have a solution that shows the bits based on the stream parameters (need to make it available e.g. in Python), if we connected it with the automatic bit allocation it'd be very useful.
And yes, "Frame shape calculator" probably suits this better. Though this might be just a preference thingy : )
Resource management is indeed a bit vague. But frame shape calculator is not quite right. The algorithm will find the frame shape that is required for the current bandwidth, but will also allocate the bitSlots in that frame. In MIPI circles we talk about bitSlot allocation.
On Thu, Jul 25, 2019 at 06:40:19PM -0500, Pierre-Louis Bossart wrote:
This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus.
This implementation is optimal for Intel platforms. Developers can also implement their own .compute_params() callback for specific resource management algorithm.
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead.
FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug
group->rates = krealloc(group->rates,
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/Makefile | 2 +- drivers/soundwire/algo_dynamic_allocation.c | 403 ++++++++++++++++++++ drivers/soundwire/bus.c | 3 + drivers/soundwire/bus.h | 46 ++- drivers/soundwire/stream.c | 20 + include/linux/soundwire/sdw.h | 5 + 6 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 drivers/soundwire/algo_dynamic_allocation.c
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 88990cac48a7..f59a9d4a28fd 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -5,7 +5,7 @@
#Bus Objs soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o \
debugfs.o
debugfs.o algo_dynamic_allocation.o
obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
diff --git a/drivers/soundwire/algo_dynamic_allocation.c b/drivers/soundwire/algo_dynamic_allocation.c new file mode 100644 index 000000000000..89edb39162b8 --- /dev/null +++ b/drivers/soundwire/algo_dynamic_allocation.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2015-18 Intel Corporation.
+/*
- Bandwidth management algorithm based on 2^n gears
- */
+#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include "bus.h"
+#define SDW_STRM_RATE_GROUPING 1
+struct sdw_group_params {
- unsigned int rate;
- int full_bw;
- int payload_bw;
- int hwidth;
+};
+struct sdw_group {
- unsigned int count;
- unsigned int max_size;
- unsigned int *rates;
+};
+struct sdw_transport_data {
- int hstart;
- int hstop;
- int block_offset;
- int sub_block_offset;
+};
+static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
struct sdw_transport_data *t_data)
+{
- struct sdw_slave_runtime *s_rt = NULL;
Superfluous initialisation.
- struct sdw_port_runtime *p_rt;
- int port_bo, sample_int;
- unsigned int rate, bps, ch = 0;
ditto for ch
- port_bo = t_data->block_offset;
- list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
sample_int = (m_rt->bus->params.curr_dr_freq / rate);
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params,
p_rt->num, true,
SDW_BLK_GRP_CNT_1,
sample_int, port_bo, port_bo >> 8,
t_data->hstart,
t_data->hstop,
I think the above two lines could fit in one
(SDW_BLK_GRP_CNT_1 * ch), 0x0);
superfluous parentheses
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
SDW_PORT_FLOW_MODE_ISOCH,
SDW_PORT_DATA_MODE_NORMAL);
port_bo += bps * ch;
}
- }
+}
+static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt,
struct sdw_group_params *params,
int port_bo, int hstop)
+{
- struct sdw_transport_data t_data = {0};
- struct sdw_port_runtime *p_rt;
- struct sdw_bus *bus = m_rt->bus;
- int sample_int, hstart = 0;
superfluous initialisation
- unsigned int rate, bps, ch, no_ch;
- rate = m_rt->stream->params.rate;
- bps = m_rt->stream->params.bps;
- ch = m_rt->ch_count;
- sample_int = (bus->params.curr_dr_freq / rate);
superfluous parentheses
- if (rate != params->rate)
return;
- t_data.hstop = hstop;
- hstart = hstop - params->hwidth + 1;
- t_data.hstart = hstart;
- list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
true, SDW_BLK_GRP_CNT_1, sample_int,
port_bo, port_bo >> 8, hstart, hstop,
(SDW_BLK_GRP_CNT_1 * no_ch), 0x0);
superfluous parentheses
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
SDW_PORT_FLOW_MODE_ISOCH,
SDW_PORT_DATA_MODE_NORMAL);
/* Check for first entry */
if (!(p_rt == list_first_entry(&m_rt->port_list,
struct sdw_port_runtime,
port_node))) {
you wanted to write "if (p_rt != ...)"
port_bo += bps * ch;
continue;
}
t_data.hstart = hstart;
t_data.hstop = hstop;
You already set these two above
t_data.block_offset = port_bo;
t_data.sub_block_offset = 0;
port_bo += bps * ch;
- }
- sdw_compute_slave_ports(m_rt, &t_data);
+}
+static void _sdw_compute_port_params(struct sdw_bus *bus,
struct sdw_group_params *params, int count)
+{
- struct sdw_master_runtime *m_rt = NULL;
superfluous initialisation
- int hstop = bus->params.col - 1;
- int block_offset, port_bo, i;
- /* Run loop for all groups to compute transport parameters */
- for (i = 0; i < count; i++) {
port_bo = 1;
block_offset = 1;
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
sdw_compute_master_ports(m_rt, ¶ms[i],
port_bo, hstop);
block_offset += m_rt->ch_count *
m_rt->stream->params.bps;
port_bo = block_offset;
}
hstop = hstop - params[i].hwidth;
hstop -= ...
- }
+}
+static int sdw_compute_group_params(struct sdw_bus *bus,
struct sdw_group_params *params,
int *rates, int count)
+{
- struct sdw_master_runtime *m_rt = NULL;
ditto
- int sel_col = bus->params.col;
- unsigned int rate, bps, ch;
- int i, column_needed = 0;
- /* Calculate bandwidth per group */
- for (i = 0; i < count; i++) {
params[i].rate = rates[i];
params[i].full_bw = bus->params.curr_dr_freq / params[i].rate;
- }
- list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
ch = m_rt->ch_count;
for (i = 0; i < count; i++) {
if (rate == params[i].rate)
params[i].payload_bw += bps * ch;
I don't know about the algorithm, rates can repeat, right? So you cannot break out of the loop here once you found one match?
}
- }
- for (i = 0; i < count; i++) {
params[i].hwidth = (sel_col *
params[i].payload_bw + params[i].full_bw - 1) /
params[i].full_bw;
column_needed += params[i].hwidth;
- }
- if (column_needed > sel_col - 1)
return -EINVAL;
- return 0;
+}
+static int sdw_add_element_group_count(struct sdw_group *group,
unsigned int rate)
+{
- int num = group->count;
- int i;
- for (i = 0; i <= num; i++) {
if (rate == group->rates[i])
Are you sure this is correct? You actually check count + 1 rates - from 0 to count inclusively. I think this isn't what you wanted to do, so my proposal below only checks count rates.
break;
if (i != num)
continue;
if (group->count >= group->max_size) {
group->max_size += 1;
group->rates = krealloc(group->rates,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!group->rates)
return -ENOMEM;
}
group->rates[group->count++] = rate;
- }
How about this:
for (i = 0; i < num; i++) if (rate == group->rates[i]) return 0;
if (group->count >= group->max_size) { group->max_size += 1; group->rates = krealloc(group->rates, (sizeof(int) * group->max_size), GFP_KERNEL); if (!group->rates) return -ENOMEM; }
group->rates[group->count++] = rate;
return 0;
- return 0;
+}
+static int sdw_get_group_count(struct sdw_bus *bus,
struct sdw_group *group)
+{
- struct sdw_master_runtime *m_rt;
- unsigned int rate;
- int ret = 0;
- group->count = 0;
- group->max_size = SDW_STRM_RATE_GROUPING;
- group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL);
- if (!group->rates)
return -ENOMEM;
- list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
if (m_rt == list_first_entry(&bus->m_rt_list,
struct sdw_master_runtime,
bus_node)) {
group->rates[group->count++] = rate;
} else {
ret = sdw_add_element_group_count(group, rate);
if (ret < 0)
Actually it looks like you should free rates here? I see that not doing this makes the caller function below easier, but I'd say this is more error- prone... Up to you but I'd go the "safe" way - if it fails, it frees itself, if it succeeds - it's freed elsewhere.
return ret;
}
- }
- return ret;
I think this will always return 0 here, so you don't need the "ret" variable in the function scope, you only need it in the "else" scope above.
+}
+/**
- sdw_compute_port_params: Compute transport and port parameters
- @bus: SDW Bus instance
- */
+static int sdw_compute_port_params(struct sdw_bus *bus) +{
- struct sdw_group_params *params = NULL;
- struct sdw_group group;
- int ret;
- ret = sdw_get_group_count(bus, &group);
- if (ret < 0)
goto out;
- if (group.count == 0)
goto out;
- params = kcalloc(group.count, sizeof(*params), GFP_KERNEL);
- if (!params) {
ret = -ENOMEM;
goto out;
- }
- /* Compute transport parameters for grouped streams */
- ret = sdw_compute_group_params(bus, params,
&group.rates[0], group.count);
- if (ret < 0)
goto out;
- _sdw_compute_port_params(bus, params, group.count);
+out:
- kfree(params);
- kfree(group.rates);
Depending whether or not you change the code above, this might change too.
- return ret;
+}
+static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) +{
- struct sdw_master_prop *prop = &bus->prop;
- int frame_int, frame_freq;
- int r, c;
- for (c = 0; c < SDW_FRAME_COLS; c++) {
for (r = 0; r < SDW_FRAME_ROWS; r++) {
if (sdw_rows[r] != prop->default_row ||
sdw_cols[c] != prop->default_col)
continue;
frame_int = sdw_rows[r] * sdw_cols[c];
frame_freq = clk_freq / frame_int;
if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) <
bus->params.bandwidth)
continue;
bus->params.row = sdw_rows[r];
bus->params.col = sdw_cols[c];
return 0;
}
- }
- return -EINVAL;
+}
+/**
- sdw_compute_bus_params: Compute bus parameters
- @bus: SDW Bus instance
- */
+static int sdw_compute_bus_params(struct sdw_bus *bus) +{
- unsigned int max_dr_freq, curr_dr_freq = 0;
- struct sdw_master_prop *mstr_prop = NULL;
superfluous initialisation
- int i, clk_values, ret;
- bool is_gear = false;
- u32 *clk_buf;
- mstr_prop = &bus->prop;
- if (!mstr_prop)
this is impossible, it's an address of bus->prop...
return -EINVAL;
- if (mstr_prop->num_clk_gears) {
clk_values = mstr_prop->num_clk_gears;
clk_buf = mstr_prop->clk_gears;
is_gear = true;
- } else if (mstr_prop->num_clk_freq) {
clk_values = mstr_prop->num_clk_freq;
clk_buf = mstr_prop->clk_freq;
- } else {
clk_values = 1;
clk_buf = NULL;
- }
- max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR;
- for (i = 0; i < clk_values; i++) {
if (!clk_buf)
curr_dr_freq = max_dr_freq;
else
curr_dr_freq = (is_gear) ?
superfluous parentheses
(max_dr_freq >> clk_buf[i]) :
ditto
clk_buf[i] * SDW_DOUBLE_RATE_FACTOR;
if (curr_dr_freq <= bus->params.bandwidth)
continue;
break;
I think this is raw code, you'd actually want to write this as
if (curr_dr_freq > bus->params.bandwidth) break;
/*
* TODO: Check all the Slave(s) port(s) audio modes and find
* whether given clock rate is supported with glitchless
* transition.
*/
- }
- if (i == clk_values)
return -EINVAL;
- ret = sdw_select_row_col(bus, curr_dr_freq);
- if (ret < 0)
return -EINVAL;
- bus->params.curr_dr_freq = curr_dr_freq;
- return 0;
+}
+/**
- sdw_compute_params: Compute bus, transport and port parameters
- @bus: SDW Bus instance
- */
+int sdw_compute_params(struct sdw_bus *bus) +{
- int ret;
- /* Computes clock frequency, frame shape and frame frequency */
- ret = sdw_compute_bus_params(bus);
- if (ret < 0) {
dev_err(bus->dev, "Compute bus params failed: %d", ret);
return ret;
- }
- /* Compute transport and port params */
- ret = sdw_compute_port_params(bus);
- if (ret < 0) {
dev_err(bus->dev, "Compute transport params failed: %d", ret);
return ret;
- }
- return 0;
+} +EXPORT_SYMBOL(sdw_compute_params); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 2354675ef104..76a180578712 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -51,6 +51,9 @@ int sdw_add_bus_master(struct sdw_bus *bus)
bus->debugfs = sdw_bus_debugfs_init(bus);
- if (!bus->compute_params)
bus->compute_params = &sdw_compute_params;
I think it is more usual to not use "&" with functions, but it's valid too
- /*
- Device numbers in SoundWire are 0 through 15. Enumeration device
- number (0), Broadcast device number (15), Group numbers (12 and
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index c57c9c23f6ca..fdb7ce034fdf 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -72,6 +72,7 @@ struct sdw_msg { };
#define SDW_DOUBLE_RATE_FACTOR 2 +#define SDW_STRM_RATE_GROUPING 1
extern int sdw_rows[SDW_FRAME_ROWS]; extern int sdw_cols[SDW_FRAME_COLS]; @@ -157,9 +158,50 @@ int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf);
+/* Retrieve and return channel count from channel mask */ +static inline int sdw_ch_mask_to_ch(int ch_mask) +{
- int c = 0;
superfluous initialisation
- for (c = 0; ch_mask; ch_mask >>= 1)
c += ch_mask & 1;
isn't there a built-in or something to count set bits... You could use ffs() to at least not loop 31 times for 0x80000000
- return c;
+}
+/* Fill transport parameter data structure */ +static inline void sdw_fill_xport_params(struct sdw_transport_params *params,
int port_num, bool grp_ctrl_valid,
int grp_ctrl, int sample_int,
int off1, int off2,
int hstart, int hstop,
int pack_mode, int lane_ctrl)
+{
- params->port_num = port_num;
- params->blk_grp_ctrl_valid = grp_ctrl_valid;
- params->blk_grp_ctrl = grp_ctrl;
- params->sample_interval = sample_int;
- params->offset1 = off1;
- params->offset2 = off2;
- params->hstart = hstart;
- params->hstop = hstop;
- params->blk_pkg_mode = pack_mode;
- params->lane_ctrl = lane_ctrl;
+}
+/* Fill port parameter data structure */ +static inline void sdw_fill_port_params(struct sdw_port_params *params,
int port_num, int bps,
int flow_mode, int data_mode)
+{
- params->num = port_num;
- params->bps = bps;
- params->flow_mode = flow_mode;
- params->data_mode = data_mode;
+}
/* Read-Modify-Write Slave register */ -static inline int -sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) +static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) { int tmp;
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 75b9ad1fb1a6..add7c121d084 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1485,6 +1485,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) bus->params.bandwidth += m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps;
/* Compute params */
if (bus->compute_params) {
ret = bus->compute_params(bus);
if (ret < 0) {
dev_err(bus->dev,
"Compute params failed: %d", ret);
return ret;
}
}
- /* Program params */ ret = sdw_program_params(bus); if (ret < 0) {
@@ -1704,6 +1714,16 @@ static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) bus->params.bandwidth -= m_rt->stream->params.rate * m_rt->ch_count * m_rt->stream->params.bps;
/* Compute params */
if (bus->compute_params) {
ret = bus->compute_params(bus);
if (ret < 0) {
dev_err(bus->dev,
"Compute params failed: %d", ret);
return ret;
}
}
- /* Program params */ ret = sdw_program_params(bus); if (ret < 0) {
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index b6acc436ac80..c7dfc824be80 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -730,6 +730,7 @@ struct sdw_master_ops {
- Bit set implies used number, bit clear implies unused number.
- @bus_lock: bus lock
- @msg_lock: message lock
- @compute_params: points to Bus resource management implementation
- @ops: Master callback ops
- @port_ops: Master port callback ops
- @params: Current bus parameters
@@ -752,6 +753,7 @@ struct sdw_bus { DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); struct mutex bus_lock; struct mutex msg_lock;
- int (*compute_params)(struct sdw_bus *bus); const struct sdw_master_ops *ops; const struct sdw_master_port_ops *port_ops; struct sdw_bus_params params;
@@ -852,6 +854,9 @@ struct sdw_stream_runtime {
struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name); void sdw_release_stream(struct sdw_stream_runtime *stream);
+int sdw_compute_params(struct sdw_bus *bus);
int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, struct sdw_port_config *port_config, -- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Thanks Guennadi for looking at this code, it's hard to review and figure things out... I replied to each, even trivial ones, to have a trace of all the issues.
+static void sdw_compute_slave_ports(struct sdw_master_runtime *m_rt,
struct sdw_transport_data *t_data)
+{
- struct sdw_slave_runtime *s_rt = NULL;
Superfluous initialisation.
ok
- struct sdw_port_runtime *p_rt;
- int port_bo, sample_int;
- unsigned int rate, bps, ch = 0;
ditto for ch
ok
- port_bo = t_data->block_offset;
- list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
sample_int = (m_rt->bus->params.curr_dr_freq / rate);
list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params,
p_rt->num, true,
SDW_BLK_GRP_CNT_1,
sample_int, port_bo, port_bo >> 8,
t_data->hstart,
t_data->hstop,
I think the above two lines could fit in one
likely yes.
(SDW_BLK_GRP_CNT_1 * ch), 0x0);
superfluous parentheses
yep
+static void sdw_compute_master_ports(struct sdw_master_runtime *m_rt,
struct sdw_group_params *params,
int port_bo, int hstop)
+{
- struct sdw_transport_data t_data = {0};
- struct sdw_port_runtime *p_rt;
- struct sdw_bus *bus = m_rt->bus;
- int sample_int, hstart = 0;
superfluous initialisation
yes
- unsigned int rate, bps, ch, no_ch;
- rate = m_rt->stream->params.rate;
- bps = m_rt->stream->params.bps;
- ch = m_rt->ch_count;
- sample_int = (bus->params.curr_dr_freq / rate);
superfluous parentheses
yes
- if (rate != params->rate)
return;
- t_data.hstop = hstop;
- hstart = hstop - params->hwidth + 1;
- t_data.hstart = hstart;
- list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
no_ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
true, SDW_BLK_GRP_CNT_1, sample_int,
port_bo, port_bo >> 8, hstart, hstop,
(SDW_BLK_GRP_CNT_1 * no_ch), 0x0);
superfluous parentheses
yes
sdw_fill_port_params(&p_rt->port_params,
p_rt->num, bps,
SDW_PORT_FLOW_MODE_ISOCH,
SDW_PORT_DATA_MODE_NORMAL);
/* Check for first entry */
if (!(p_rt == list_first_entry(&m_rt->port_list,
struct sdw_port_runtime,
port_node))) {
you wanted to write "if (p_rt != ...)"
bad code indeed, thanks for spotting this. I need to double-check this one, now I don't trust the == it could also be that it was meant to be a NULL check on the first entry.
port_bo += bps * ch;
continue;
}
t_data.hstart = hstart;
t_data.hstop = hstop;
You already set these two above
need to check this as well.
t_data.block_offset = port_bo;
t_data.sub_block_offset = 0;
port_bo += bps * ch;
- }
- sdw_compute_slave_ports(m_rt, &t_data);
+}
+static void _sdw_compute_port_params(struct sdw_bus *bus,
struct sdw_group_params *params, int count)
+{
- struct sdw_master_runtime *m_rt = NULL;
superfluous initialisation
yes
- int hstop = bus->params.col - 1;
- int block_offset, port_bo, i;
- /* Run loop for all groups to compute transport parameters */
- for (i = 0; i < count; i++) {
port_bo = 1;
block_offset = 1;
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
sdw_compute_master_ports(m_rt, ¶ms[i],
port_bo, hstop);
block_offset += m_rt->ch_count *
m_rt->stream->params.bps;
port_bo = block_offset;
}
hstop = hstop - params[i].hwidth;
hstop -= ...
yes
- }
+}
+static int sdw_compute_group_params(struct sdw_bus *bus,
struct sdw_group_params *params,
int *rates, int count)
+{
- struct sdw_master_runtime *m_rt = NULL;
ditto
yes
- int sel_col = bus->params.col;
- unsigned int rate, bps, ch;
- int i, column_needed = 0;
- /* Calculate bandwidth per group */
- for (i = 0; i < count; i++) {
params[i].rate = rates[i];
params[i].full_bw = bus->params.curr_dr_freq / params[i].rate;
- }
- list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
bps = m_rt->stream->params.bps;
ch = m_rt->ch_count;
for (i = 0; i < count; i++) {
if (rate == params[i].rate)
params[i].payload_bw += bps * ch;
I don't know about the algorithm, rates can repeat, right? So you cannot break out of the loop here once you found one match?
This code looks wrong. Need to get an intravenous caffeine injection.
}
- }
- for (i = 0; i < count; i++) {
params[i].hwidth = (sel_col *
params[i].payload_bw + params[i].full_bw - 1) /
params[i].full_bw;
column_needed += params[i].hwidth;
- }
- if (column_needed > sel_col - 1)
return -EINVAL;
- return 0;
+}
+static int sdw_add_element_group_count(struct sdw_group *group,
unsigned int rate)
+{
- int num = group->count;
- int i;
- for (i = 0; i <= num; i++) {
if (rate == group->rates[i])
Are you sure this is correct? You actually check count + 1 rates - from 0 to count inclusively. I think this isn't what you wanted to do, so my proposal below only checks count rates.
I'll have to double check. There's already the warning on krealloc that looks suspicious as well.
break;
if (i != num)
continue;
if (group->count >= group->max_size) {
group->max_size += 1;
group->rates = krealloc(group->rates,
(sizeof(int) * group->max_size),
GFP_KERNEL);
if (!group->rates)
return -ENOMEM;
}
group->rates[group->count++] = rate;
- }
How about this:
for (i = 0; i < num; i++) if (rate == group->rates[i]) return 0;
if (group->count >= group->max_size) { group->max_size += 1; group->rates = krealloc(group->rates, (sizeof(int) * group->max_size), GFP_KERNEL); if (!group->rates) return -ENOMEM; }
group->rates[group->count++] = rate;
return 0;
will check offline and see.
- return 0;
+}
+static int sdw_get_group_count(struct sdw_bus *bus,
struct sdw_group *group)
+{
- struct sdw_master_runtime *m_rt;
- unsigned int rate;
- int ret = 0;
- group->count = 0;
- group->max_size = SDW_STRM_RATE_GROUPING;
- group->rates = kcalloc(group->max_size, sizeof(int), GFP_KERNEL);
- if (!group->rates)
return -ENOMEM;
- list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
rate = m_rt->stream->params.rate;
if (m_rt == list_first_entry(&bus->m_rt_list,
struct sdw_master_runtime,
bus_node)) {
group->rates[group->count++] = rate;
} else {
ret = sdw_add_element_group_count(group, rate);
if (ret < 0)
Actually it looks like you should free rates here? I see that not doing this makes the caller function below easier, but I'd say this is more error- prone... Up to you but I'd go the "safe" way - if it fails, it frees itself, if it succeeds - it's freed elsewhere.
good catch, will look into this.
return ret;
}
- }
- return ret;
I think this will always return 0 here, so you don't need the "ret" variable in the function scope, you only need it in the "else" scope above.
will check. In general I avoid per-scope declarations.
+}
+/**
- sdw_compute_port_params: Compute transport and port parameters
- @bus: SDW Bus instance
- */
+static int sdw_compute_port_params(struct sdw_bus *bus) +{
- struct sdw_group_params *params = NULL;
- struct sdw_group group;
- int ret;
- ret = sdw_get_group_count(bus, &group);
- if (ret < 0)
goto out;
- if (group.count == 0)
goto out;
- params = kcalloc(group.count, sizeof(*params), GFP_KERNEL);
- if (!params) {
ret = -ENOMEM;
goto out;
- }
- /* Compute transport parameters for grouped streams */
- ret = sdw_compute_group_params(bus, params,
&group.rates[0], group.count);
- if (ret < 0)
goto out;
- _sdw_compute_port_params(bus, params, group.count);
+out:
- kfree(params);
- kfree(group.rates);
Depending whether or not you change the code above, this might change too.
will check
+static int sdw_compute_bus_params(struct sdw_bus *bus) +{
- unsigned int max_dr_freq, curr_dr_freq = 0;
- struct sdw_master_prop *mstr_prop = NULL;
superfluous initialisation
yes
- int i, clk_values, ret;
- bool is_gear = false;
- u32 *clk_buf;
- mstr_prop = &bus->prop;
- if (!mstr_prop)
this is impossible, it's an address of bus->prop...
Gah, yes.
return -EINVAL;
- if (mstr_prop->num_clk_gears) {
clk_values = mstr_prop->num_clk_gears;
clk_buf = mstr_prop->clk_gears;
is_gear = true;
- } else if (mstr_prop->num_clk_freq) {
clk_values = mstr_prop->num_clk_freq;
clk_buf = mstr_prop->clk_freq;
- } else {
clk_values = 1;
clk_buf = NULL;
- }
- max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR;
- for (i = 0; i < clk_values; i++) {
if (!clk_buf)
curr_dr_freq = max_dr_freq;
else
curr_dr_freq = (is_gear) ?
superfluous parentheses
(max_dr_freq >> clk_buf[i]) :
ditto
yes and yes
clk_buf[i] * SDW_DOUBLE_RATE_FACTOR;
if (curr_dr_freq <= bus->params.bandwidth)
continue;
break;
I think this is raw code, you'd actually want to write this as
if (curr_dr_freq > bus->params.bandwidth) break;
I saw this before my Summer break and forgot about it. yes it needs to be fixed.
bus->debugfs = sdw_bus_debugfs_init(bus);
- if (!bus->compute_params)
bus->compute_params = &sdw_compute_params;
I think it is more usual to not use "&" with functions, but it's valid too
yes, will fix
+/* Retrieve and return channel count from channel mask */ +static inline int sdw_ch_mask_to_ch(int ch_mask) +{
- int c = 0;
superfluous initialisation
yes
- for (c = 0; ch_mask; ch_mask >>= 1)
c += ch_mask & 1;
isn't there a built-in or something to count set bits... You could use ffs() to at least not loop 31 times for 0x80000000
will check
On Thu, Jul 25, 2019 at 06:40:19PM -0500, Pierre-Louis Bossart wrote:
This algorithm computes bus parameters like clock frequency, frame shape and port transport parameters based on active stream(s) running on the bus.
This implementation is optimal for Intel platforms. Developers can also implement their own .compute_params() callback for specific resource management algorithm.
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. All hard-coded values were removed from the initial contribution to use BIOS information instead.
FIXME: remove checkpatch report WARNING: Reusing the krealloc arg is almost always a bug
group->rates = krealloc(group->rates,
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/Makefile | 2 +- drivers/soundwire/algo_dynamic_allocation.c | 403 ++++++++++++++++++++ drivers/soundwire/bus.c | 3 + drivers/soundwire/bus.h | 46 ++- drivers/soundwire/stream.c | 20 + include/linux/soundwire/sdw.h | 5 + 6 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 drivers/soundwire/algo_dynamic_allocation.c
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 88990cac48a7..f59a9d4a28fd 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -5,7 +5,7 @@
#Bus Objs soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o \
debugfs.o
debugfs.o algo_dynamic_allocation.o
obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o
diff --git a/drivers/soundwire/algo_dynamic_allocation.c b/drivers/soundwire/algo_dynamic_allocation.c new file mode 100644 index 000000000000..89edb39162b8 --- /dev/null +++ b/drivers/soundwire/algo_dynamic_allocation.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2015-18 Intel Corporation.
+/*
- Bandwidth management algorithm based on 2^n gears
- */
+#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include "bus.h"
+#define SDW_STRM_RATE_GROUPING 1
+struct sdw_group_params {
- unsigned int rate;
- int full_bw;
- int payload_bw;
- int hwidth;
+};
+struct sdw_group {
- unsigned int count;
- unsigned int max_size;
- unsigned int *rates;
+};
+struct sdw_transport_data {
- int hstart;
- int hstop;
- int block_offset;
- int sub_block_offset;
+};
+/**
- sdw_compute_port_params: Compute transport and port parameters
- @bus: SDW Bus instance
- */
+static int sdw_compute_port_params(struct sdw_bus *bus) +{
- struct sdw_group_params *params = NULL;
- struct sdw_group group;
- int ret;
- ret = sdw_get_group_count(bus, &group);
- if (ret < 0)
goto out;
- if (group.count == 0)
goto out;
- params = kcalloc(group.count, sizeof(*params), GFP_KERNEL);
- if (!params) {
ret = -ENOMEM;
goto out;
- }
- /* Compute transport parameters for grouped streams */
- ret = sdw_compute_group_params(bus, params,
&group.rates[0], group.count);
- if (ret < 0)
goto out;
- _sdw_compute_port_params(bus, params, group.count);
+out:
- kfree(params);
- kfree(group.rates);
- return ret;
+}
+static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) +{
- struct sdw_master_prop *prop = &bus->prop;
- int frame_int, frame_freq;
- int r, c;
- for (c = 0; c < SDW_FRAME_COLS; c++) {
for (r = 0; r < SDW_FRAME_ROWS; r++) {
if (sdw_rows[r] != prop->default_row ||
sdw_cols[c] != prop->default_col)
continue;
Are we only supporting default rows and cols?
frame_int = sdw_rows[r] * sdw_cols[c];
frame_freq = clk_freq / frame_int;
if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) <
bus->params.bandwidth)
continue;
bus->params.row = sdw_rows[r];
bus->params.col = sdw_cols[c];
return 0;
}
- }
- return -EINVAL;
+}
-- 2.20.1
--
+static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) +{
- struct sdw_master_prop *prop = &bus->prop;
- int frame_int, frame_freq;
- int r, c;
- for (c = 0; c < SDW_FRAME_COLS; c++) {
for (r = 0; r < SDW_FRAME_ROWS; r++) {
if (sdw_rows[r] != prop->default_row ||
sdw_cols[c] != prop->default_col)
continue;
Are we only supporting default rows and cols?
for now yes. Note that the default is defined by firmware and e.g. different for ICL (50x4) and CML (125x2). The firmware itself also provides a single clock value so we'd need to override the DSDT or force the properties to be different to use multiple gears.
This will probably change at some point when we have multiple device per link. SoundWire 1.2 devices also provide a standard means to control the clock, otherwise with SoundWire 1.1 the clock management requires quite a bit of imp-def changes that we have not tested.
frame_int = sdw_rows[r] * sdw_cols[c];
frame_freq = clk_freq / frame_int;
if ((clk_freq - (frame_freq * SDW_FRAME_CTRL_BITS)) <
bus->params.bandwidth)
continue;
bus->params.row = sdw_rows[r];
bus->params.col = sdw_cols[c];
return 0;
}
- }
- return -EINVAL;
+}
-- 2.20.1
On most hardware platforms, SoundWire interfaces are pin-muxed with other interfaces (typically DMIC or I2S) and the status of each link needs to be checked at boot time.
For Intel platforms, the BIOS provides a menu to enable/disable the links separately, and the information is provided to the OS with an Intel-specific _DSD property. The same capability will be added to revisions of the MIPI DisCo specification.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 26 ++++++++++++++++++++++---- include/linux/soundwire/sdw.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 796ac2bc8cea..5947fa8e840b 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -90,6 +90,8 @@ #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
+#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) + enum intel_pdi_type { INTEL_PDI_IN = 0, INTEL_PDI_OUT = 1, @@ -922,7 +924,7 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) struct sdw_master_prop *prop = &bus->prop; struct fwnode_handle *link; char name[32]; - int nval, i; + u32 quirk_mask;
/* Find master handle */ snprintf(name, sizeof(name), @@ -937,6 +939,14 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) fwnode_property_read_u32(link, "intel-sdw-ip-clock", &prop->mclk_freq); + + fwnode_property_read_u32(link, + "intel-quirk-mask", + &quirk_mask); + + if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) + prop->hw_disabled = true; + return 0; }
@@ -997,6 +1007,12 @@ static int intel_probe(struct platform_device *pdev) goto err_master_reg; }
+ if (sdw->cdns.bus.prop.hw_disabled) { + dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n", + sdw->cdns.bus.link_id); + return 0; + } + /* Initialize shim and controller */ intel_link_power_up(sdw); intel_shim_init(sdw); @@ -1050,9 +1066,11 @@ static int intel_remove(struct platform_device *pdev)
sdw = platform_get_drvdata(pdev);
- intel_debugfs_exit(sdw); - free_irq(sdw->res->irq, sdw); - snd_soc_unregister_component(sdw->cdns.dev); + if (!sdw->cdns.bus.prop.hw_disabled) { + intel_debugfs_exit(sdw); + free_irq(sdw->res->irq, sdw); + snd_soc_unregister_component(sdw->cdns.dev); + } sdw_delete_bus_master(&sdw->cdns.bus);
return 0; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index c7dfc824be80..f78b076a8782 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -380,6 +380,7 @@ struct sdw_slave_prop { * @err_threshold: Number of times that software may retry sending a single * command * @mclk_freq: clock reference passed to SoundWire Master, in Hz. + * @hw_disabled: if true, the Master is not functional, typically due to pin-mux */ struct sdw_master_prop { u32 revision; @@ -395,6 +396,7 @@ struct sdw_master_prop { bool dynamic_frame; u32 err_threshold; u32 mclk_freq; + bool hw_disabled; };
int sdw_master_read_prop(struct sdw_bus *bus);
On Thu, Jul 25, 2019 at 06:40:20PM -0500, Pierre-Louis Bossart wrote:
On most hardware platforms, SoundWire interfaces are pin-muxed with other interfaces (typically DMIC or I2S) and the status of each link needs to be checked at boot time.
For Intel platforms, the BIOS provides a menu to enable/disable the links separately, and the information is provided to the OS with an Intel-specific _DSD property. The same capability will be added to revisions of the MIPI DisCo specification.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 26 ++++++++++++++++++++++---- include/linux/soundwire/sdw.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 796ac2bc8cea..5947fa8e840b 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -90,6 +90,8 @@ #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
+#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
enum intel_pdi_type { INTEL_PDI_IN = 0, INTEL_PDI_OUT = 1, @@ -922,7 +924,7 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) struct sdw_master_prop *prop = &bus->prop; struct fwnode_handle *link; char name[32];
- int nval, i;
u32 quirk_mask;
/* Find master handle */ snprintf(name, sizeof(name),
@@ -937,6 +939,14 @@ static int sdw_master_read_intel_prop(struct sdw_bus *bus) fwnode_property_read_u32(link, "intel-sdw-ip-clock", &prop->mclk_freq);
- fwnode_property_read_u32(link,
"intel-quirk-mask",
&quirk_mask);
- if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
prop->hw_disabled = true;
- return 0;
}
@@ -997,6 +1007,12 @@ static int intel_probe(struct platform_device *pdev) goto err_master_reg; }
- if (sdw->cdns.bus.prop.hw_disabled) {
dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n",
sdw->cdns.bus.link_id);
return 0;
- }
- /* Initialize shim and controller */ intel_link_power_up(sdw); intel_shim_init(sdw);
@@ -1050,9 +1066,11 @@ static int intel_remove(struct platform_device *pdev)
sdw = platform_get_drvdata(pdev);
- intel_debugfs_exit(sdw);
- free_irq(sdw->res->irq, sdw);
- snd_soc_unregister_component(sdw->cdns.dev);
if (!sdw->cdns.bus.prop.hw_disabled) {
intel_debugfs_exit(sdw);
free_irq(sdw->res->irq, sdw);
snd_soc_unregister_component(sdw->cdns.dev);
} sdw_delete_bus_master(&sdw->cdns.bus);
return 0;
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index c7dfc824be80..f78b076a8782 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -380,6 +380,7 @@ struct sdw_slave_prop {
- @err_threshold: Number of times that software may retry sending a single
- command
- @mclk_freq: clock reference passed to SoundWire Master, in Hz.
*/
- @hw_disabled: if true, the Master is not functional, typically due to pin-mux
struct sdw_master_prop { u32 revision; @@ -395,6 +396,7 @@ struct sdw_master_prop { bool dynamic_frame; u32 err_threshold; u32 mclk_freq;
- bool hw_disabled;
Do we have such cases where some of SoundWire links are disabled and some enabled?
};
int sdw_master_read_prop(struct sdw_bus *bus);
2.20.1
--
On 8/5/19 11:57 AM, Sanyog Kale wrote:
On Thu, Jul 25, 2019 at 06:40:20PM -0500, Pierre-Louis Bossart wrote:
On most hardware platforms, SoundWire interfaces are pin-muxed with other interfaces (typically DMIC or I2S) and the status of each link needs to be checked at boot time.
For Intel platforms, the BIOS provides a menu to enable/disable the links separately, and the information is provided to the OS with an Intel-specific _DSD property. The same capability will be added to revisions of the MIPI DisCo specification.
[snip]
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index c7dfc824be80..f78b076a8782 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -380,6 +380,7 @@ struct sdw_slave_prop {
- @err_threshold: Number of times that software may retry sending a single
- command
- @mclk_freq: clock reference passed to SoundWire Master, in Hz.
*/ struct sdw_master_prop { u32 revision;
- @hw_disabled: if true, the Master is not functional, typically due to pin-mux
@@ -395,6 +396,7 @@ struct sdw_master_prop { bool dynamic_frame; u32 err_threshold; u32 mclk_freq;
- bool hw_disabled;
Do we have such cases where some of SoundWire links are disabled and some enabled?
Yes, by default my ICL test board uses HDaudio for the codec so the SoundWire link0 is disabled. If I rework the board and change the BIOS advanced menu then SoundWire link0 is enabled. This information is dynamically provided to the OS after the _INI step. SoundWire-2/3 are used typically for attached DMICs or for a combination of SoundWire amplifier and mic capture. It's really platform-specific.
};
int sdw_master_read_prop(struct sdw_bus *bus);
2.20.1
The hardware and ACPI info may report the presence of links that are not physically enabled (e.g. due to pin-muxing or hardware reworks), which in turn can result in errors being thrown. This shouldn't be the case for production devices but will happen a lot on development devices - even more so when they expose a connector.
Add a module parameter to filter out such links, e.g. adding the following config to a file in /etc/modprobe.d will select the second and third links only.
options soundwire_intel_init sdw_link_mask=0x6
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel_init.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 70637a0383d2..6ae8bb13f907 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -22,6 +22,10 @@ #define SDW_LINK_BASE 0x30000 #define SDW_LINK_SIZE 0x10000
+static int link_mask; +module_param_named(sdw_link_mask, link_mask, int, 0444); +MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); + struct sdw_link_data { struct sdw_intel_link_res res; struct platform_device *pdev; @@ -83,6 +87,9 @@ static struct sdw_intel_ctx caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); caps &= GENMASK(2, 0);
+ dev_dbg(&adev->dev, "SoundWire links: BIOS count %d hardware caps %d\n", + count, caps); + /* Check HW supported vs property value and use min of two */ count = min_t(u8, caps, count);
@@ -111,6 +118,13 @@ static struct sdw_intel_ctx
/* Create SDW Master devices */ for (i = 0; i < count; i++) { + if (link_mask && !(link_mask & BIT(i))) { + dev_dbg(&adev->dev, + "Link %d masked, will not be enabled\n", i); + link++; + continue; + } + link->res.irq = res->irq; link->res.registers = res->mmio_base + SDW_LINK_BASE + (SDW_LINK_SIZE * i);
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
@@ -83,6 +87,9 @@ static struct sdw_intel_ctx caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); caps &= GENMASK(2, 0);
- dev_dbg(&adev->dev, "SoundWire links: BIOS count %d hardware caps %d\n",
count, caps);
- /* Check HW supported vs property value and use min of two */ count = min_t(u8, caps, count);
This message does not look like it belongs to current patch - no link_mask dependency whatsoever. There have been couple "informative" patches in your series, maybe schedule it with them instead (as a separate series)?
On 7/26/19 5:30 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
@@ -83,6 +87,9 @@ static struct sdw_intel_ctx caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); caps &= GENMASK(2, 0); + dev_dbg(&adev->dev, "SoundWire links: BIOS count %d hardware caps %d\n", + count, caps);
/* Check HW supported vs property value and use min of two */ count = min_t(u8, caps, count);
This message does not look like it belongs to current patch - no link_mask dependency whatsoever. There have been couple "informative" patches in your series, maybe schedule it with them instead (as a separate series)?
You're right, this log should be in a different patch. it was added when I was debugging the DisCo properties a couple of months back and should be moved. thanks for noting this.
The code has a set of defaults which may not be relevant in all cases, add kernel parameter as a helper - mostly for early board bring-up.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 18c6ac026e85..dede55072191 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -20,6 +20,10 @@ #include "bus.h" #include "cadence_master.h"
+static int interrupt_mask; +module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444); +MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); + #define CDNS_MCP_CONFIG 0x0
#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24) @@ -830,6 +834,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) /* now enable all of the above */ mask |= CDNS_MCP_INT_IRQ;
+ if (interrupt_mask) /* parameter override */ + mask = interrupt_mask; + cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
return do_reset(cdns);
All DAI callbacks are in intel.c except for shutdown. Move and remove export symbol
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 14 -------------- drivers/soundwire/cadence_master.h | 2 -- drivers/soundwire/intel.c | 17 +++++++++++++++-- 3 files changed, 15 insertions(+), 18 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index dede55072191..4a189e487830 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1381,19 +1381,5 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, } EXPORT_SYMBOL(sdw_cdns_alloc_stream);
-void sdw_cdns_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct sdw_cdns_dma_data *dma; - - dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return; - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(dma); -} -EXPORT_SYMBOL(sdw_cdns_shutdown); - MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Cadence Soundwire Library"); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index d375bbfead18..de97bc22acb7 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -177,8 +177,6 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port, u32 ch, u32 dir, struct sdw_cdns_pdi *pdi);
-void sdw_cdns_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai); int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai, void *stream, int direction); int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai, diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 5947fa8e840b..c40ab443e723 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -772,6 +772,19 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; }
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma; + + dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return; + + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +} + static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction) { @@ -787,14 +800,14 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_params = intel_hw_params, .hw_free = intel_hw_free, - .shutdown = sdw_cdns_shutdown, + .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, };
static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_params = intel_hw_params, .hw_free = intel_hw_free, - .shutdown = sdw_cdns_shutdown, + .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, };
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+void intel_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct sdw_cdns_dma_data *dma;
- dma = snd_soc_dai_get_dma_data(dai, substream);
- if (!dma)
return;
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(dma);
+}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
On 7/26/19 5:38 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma;
+ dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return;
+ snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
I must admit I have no idea why we have a reference to DMAs here, this looks like an abuse to store a dai-specific context, and the initial test looks like copy-paste to detect invalid configs, as done in other callbacks. Vinod and Sanyog might have more history than me here.
On 26-07-19, 09:46, Pierre-Louis Bossart wrote:
On 7/26/19 5:38 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma;
+ dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return;
+ snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
I must admit I have no idea why we have a reference to DMAs here, this looks like an abuse to store a dai-specific context, and the initial test looks like copy-paste to detect invalid configs, as done in other callbacks. Vinod and Sanyog might have more history than me here.
I dont see snd_soc_dai_set_dma_data() call for sdw_cdns_dma_data so somthing is missing (at least in upstream code)
IIRC we should have a snd_soc_dai_set_dma_data() in alloc or some initialization routine and we free it here.. Sanyog?
On 8/2/19 12:28 PM, Vinod Koul wrote:
On 26-07-19, 09:46, Pierre-Louis Bossart wrote:
On 7/26/19 5:38 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma;
+ dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return;
+ snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
I must admit I have no idea why we have a reference to DMAs here, this looks like an abuse to store a dai-specific context, and the initial test looks like copy-paste to detect invalid configs, as done in other callbacks. Vinod and Sanyog might have more history than me here.
I dont see snd_soc_dai_set_dma_data() call for sdw_cdns_dma_data so somthing is missing (at least in upstream code)
IIRC we should have a snd_soc_dai_set_dma_data() in alloc or some initialization routine and we free it here.. Sanyog?
the code does a bunch of get_dma_data() and this seems to work, but indeed I don't see where the _set_dma_data() is done. magic.
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma;
+ dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return;
+ snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
I must admit I have no idea why we have a reference to DMAs here, this looks like an abuse to store a dai-specific context, and the initial test looks like copy-paste to detect invalid configs, as done in other callbacks. Vinod and Sanyog might have more history than me here.
I dont see snd_soc_dai_set_dma_data() call for sdw_cdns_dma_data so somthing is missing (at least in upstream code)
IIRC we should have a snd_soc_dai_set_dma_data() in alloc or some initialization routine and we free it here.. Sanyog?
Vinod, I double-checked that we do not indeed have a call to snd_soc_dai_dma_data(), but there is code in cdns_set_stream() that sets the relevant dai->playback/capture_dma_data, see below
I am not a big fan of this code, touching the ASoC core internal fields isn't a good idea in general.
Also not sure why for a DAI we need both _drvdata and _dma_data (especially for this case where the information stored has absolutely nothing to do with DMAs).
If the idea was to keep a context that is direction-dependent, that's likely unnecessary. For the Intel/Cadence case the interfaces can be configured as playback OR capture, not both concurrently, so the "dma" information could have been stored in the generic DAI _drvdata.
I have other things to look into for now but this code will likely need to be cleaned-up at some point to remove unnecessary parts.
int cdns_set_sdw_stream(struct snd_soc_dai *dai, void *stream, bool pcm, int direction) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma;
dma = kzalloc(sizeof(*dma), GFP_KERNEL); if (!dma) return -ENOMEM;
if (pcm) dma->stream_type = SDW_STREAM_PCM; else dma->stream_type = SDW_STREAM_PDM;
dma->bus = &cdns->bus; dma->link_id = cdns->instance;
dma->stream = stream;
this is equivalent to snd_soc_dai_dma_data()
if (direction == SNDRV_PCM_STREAM_PLAYBACK) dai->playback_dma_data = dma; else dai->capture_dma_data = dma; <<<< return 0; } EXPORT_SYMBOL(cdns_set_sdw_stream);
On 14-08-19, 14:31, Pierre-Louis Bossart wrote:
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma;
+ dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return;
+ snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
I must admit I have no idea why we have a reference to DMAs here, this looks like an abuse to store a dai-specific context, and the initial test looks like copy-paste to detect invalid configs, as done in other callbacks. Vinod and Sanyog might have more history than me here.
I dont see snd_soc_dai_set_dma_data() call for sdw_cdns_dma_data so somthing is missing (at least in upstream code)
IIRC we should have a snd_soc_dai_set_dma_data() in alloc or some initialization routine and we free it here.. Sanyog?
Vinod, I double-checked that we do not indeed have a call to snd_soc_dai_dma_data(), but there is code in cdns_set_stream() that sets the relevant dai->playback/capture_dma_data, see below
I am not a big fan of this code, touching the ASoC core internal fields isn't a good idea in general.
IIRC as long as you stick to single link I do not see this required. The question comes into picture when we have multi links as you would need to allocate a soundwire stream and set that for all the sdw DAIs
So, what is the current model of soundwire stream, which entity allocates that and do you still care about multi-link? is there any machine driver with soundwire upstream yet?
Also not sure why for a DAI we need both _drvdata and _dma_data (especially
_drvdata is global for driver whereas _dma_data is typically used per DAI
for this case where the information stored has absolutely nothing to do with DMAs).
If the idea was to keep a context that is direction-dependent, that's likely unnecessary. For the Intel/Cadence case the interfaces can be configured as playback OR capture, not both concurrently, so the "dma" information could have been stored in the generic DAI _drvdata.
I have other things to look into for now but this code will likely need to be cleaned-up at some point to remove unnecessary parts.
Sure please go ahead and do the cleanup.
int cdns_set_sdw_stream(struct snd_soc_dai *dai, void *stream, bool pcm, int direction) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma;
dma = kzalloc(sizeof(*dma), GFP_KERNEL); if (!dma) return -ENOMEM;
if (pcm) dma->stream_type = SDW_STREAM_PCM; else dma->stream_type = SDW_STREAM_PDM;
dma->bus = &cdns->bus; dma->link_id = cdns->instance;
dma->stream = stream;
this is equivalent to snd_soc_dai_dma_data()
if (direction == SNDRV_PCM_STREAM_PLAYBACK) dai->playback_dma_data = dma; else dai->capture_dma_data = dma; <<<< return 0; } EXPORT_SYMBOL(cdns_set_sdw_stream);
+void intel_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns_dma_data *dma;
+ dma = snd_soc_dai_get_dma_data(dai, substream); + if (!dma) + return;
+ snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma); +}
Correct me if I'm wrong, but do we really need to _get_dma_ here? _set_dma_ seems bulletproof, same for kfree.
I must admit I have no idea why we have a reference to DMAs here, this looks like an abuse to store a dai-specific context, and the initial test looks like copy-paste to detect invalid configs, as done in other callbacks. Vinod and Sanyog might have more history than me here.
I dont see snd_soc_dai_set_dma_data() call for sdw_cdns_dma_data so somthing is missing (at least in upstream code)
IIRC we should have a snd_soc_dai_set_dma_data() in alloc or some initialization routine and we free it here.. Sanyog?
Vinod, I double-checked that we do not indeed have a call to snd_soc_dai_dma_data(), but there is code in cdns_set_stream() that sets the relevant dai->playback/capture_dma_data, see below
I am not a big fan of this code, touching the ASoC core internal fields isn't a good idea in general.
IIRC as long as you stick to single link I do not see this required. The question comes into picture when we have multi links as you would need to allocate a soundwire stream and set that for all the sdw DAIs
So, what is the current model of soundwire stream, which entity allocates that and do you still care about multi-link? is there any machine driver with soundwire upstream yet?
yes, multi-link is definitively required and one of the main appeals of SoundWire. We have a platform with 2 amplifiers on separate links and they need to be synchronized and handled with the stream concept.
The tentative plan would be to move the stream allocation to the dailink .init (or equivalent), and make sure each DAI in that link used the same stream information. There are dependencies on the multi-cpu concept that Morimoto-san wanted to push, so we'll likely be the first users.
For the DAI trigger, we will need to change the existing API so that a sdw_stream_enable() can be called multiple times, but only takes effect when the .trigger of the first DAI in the stream is invoked. This is a similar behavior than with HDaudio .trigger operations when the SYNC bits are used.
We will do this when we have a first pass working with all codec drivers upstream and a basic machine driver upstream with all 4 links working independently.
Everything is done in public btw, you can track our WIP solutions here:
https://github.com/thesofproject/linux/pull/1140 https://github.com/thesofproject/linux/pull/1141 https://github.com/thesofproject/linux/pull/1142
Move code to helper for reuse in power management routines
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c40ab443e723..215dc81cdf73 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -984,6 +984,15 @@ static struct sdw_master_ops sdw_intel_ops = { .post_bank_switch = intel_post_bank_switch, };
+static int intel_init(struct sdw_intel *sdw) +{ + /* Initialize shim and controller */ + intel_link_power_up(sdw); + intel_shim_init(sdw); + + return sdw_cdns_init(&sdw->cdns); +} + /* * probe and init */ @@ -1026,11 +1035,8 @@ static int intel_probe(struct platform_device *pdev) return 0; }
- /* Initialize shim and controller */ - intel_link_power_up(sdw); - intel_shim_init(sdw); - - ret = sdw_cdns_init(&sdw->cdns); + /* Initialize shim, controller and Cadence IP */ + ret = intel_init(sdw); if (ret) goto err_init;
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
Move code to helper for reuse in power management routines
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c40ab443e723..215dc81cdf73 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -984,6 +984,15 @@ static struct sdw_master_ops sdw_intel_ops = { .post_bank_switch = intel_post_bank_switch, };
+static int intel_init(struct sdw_intel *sdw) +{
- /* Initialize shim and controller */
- intel_link_power_up(sdw);
- intel_shim_init(sdw);
- return sdw_cdns_init(&sdw->cdns);
+}
Why don't we check polling status for _link_power_up? I've already met slow starting devices in the past. If polling fails and -EAGAIN is returned, flow of initialization should react appropriately e.g. poll till MAX_TIMEOUT of some sort -or- collapse.
On 7/26/19 5:42 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
Move code to helper for reuse in power management routines
Signed-off-by: Pierre-Louis Bossart
pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index c40ab443e723..215dc81cdf73 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -984,6 +984,15 @@ static struct sdw_master_ops sdw_intel_ops = { .post_bank_switch = intel_post_bank_switch, }; +static int intel_init(struct sdw_intel *sdw) +{ + /* Initialize shim and controller */ + intel_link_power_up(sdw); + intel_shim_init(sdw);
+ return sdw_cdns_init(&sdw->cdns); +}
Why don't we check polling status for _link_power_up? I've already met slow starting devices in the past. If polling fails and -EAGAIN is returned, flow of initialization should react appropriately e.g. poll till MAX_TIMEOUT of some sort -or- collapse.
The code does what it states, it initializes the Intel and Cadence IPs.
What you are referring to is a different problem: once the bus starts, then Slave devices will report as attached, and will be enumerated. This will take time. The devices I tested show up immediately and within a couple of ms the bus is operational. But MIPI allows to the sync to take up to 4096 frames, which is 85ms with a 48kHz frame rate, so yes we do need to look into this.
We currently do not have a notification that tells us the bus is back to normal, that's a design flaw - see the last patch of the series where I kicked the can down the road but adding an artificial delay.
So yes good point indeed but on the wrong patch :-)
Implement suspend/resume capabilities (not runtime_pm for now)
Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 215dc81cdf73..1477c35f616f 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <sound/pcm_params.h> +#include <linux/pm_runtime.h> #include <sound/soc.h> #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw.h> @@ -278,6 +279,35 @@ static void intel_debugfs_exit(struct sdw_intel *sdw) /* * shim ops */ +static int intel_link_power_down(struct sdw_intel *sdw) +{ + int link_control, spa_mask, cpa_mask, ret; + unsigned int link_id = sdw->instance; + void __iomem *shim = sdw->res->shim; + u16 ioctl; + + /* Glue logic */ + ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); + ioctl |= SDW_SHIM_IOCTL_BKE; + ioctl |= SDW_SHIM_IOCTL_COE; + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + + ioctl &= ~(SDW_SHIM_IOCTL_MIF); + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + + /* Link power down sequence */ + link_control = intel_readl(shim, SDW_SHIM_LCTL); + spa_mask = ~(SDW_SHIM_LCTL_SPA << link_id); + cpa_mask = (SDW_SHIM_LCTL_CPA << link_id); + link_control &= spa_mask; + + ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); + if (ret < 0) + return ret; + + sdw->cdns.link_up = false; + return 0; +}
static int intel_link_power_up(struct sdw_intel *sdw) { @@ -300,6 +330,29 @@ static int intel_link_power_up(struct sdw_intel *sdw) return 0; }
+static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) +{ + void __iomem *shim = sdw->res->shim; + unsigned int link_id = sdw->instance; + u16 wake_en, wake_sts; + + if (wake_enable) { + /* Enable the wakeup */ + intel_writew(shim, SDW_SHIM_WAKEEN, + (SDW_SHIM_WAKEEN_ENABLE << link_id)); + } else { + /* Disable the wake up interrupt */ + wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); + wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); + + /* Clear wake status */ + wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); + wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); + } +} + static int intel_shim_init(struct sdw_intel *sdw) { void __iomem *shim = sdw->res->shim; @@ -1095,11 +1148,60 @@ static int intel_remove(struct platform_device *pdev) return 0; }
+/* + * PM calls + */ + +#ifdef CONFIG_PM + +static int intel_suspend(struct device *dev) +{ + struct sdw_intel *sdw; + int ret; + + sdw = dev_get_drvdata(dev); + + ret = intel_link_power_down(sdw); + if (ret) { + dev_err(dev, "Link power down failed: %d", ret); + return ret; + } + + intel_shim_wake(sdw, false); + + return 0; +} + +static int intel_resume(struct device *dev) +{ + struct sdw_intel *sdw; + int ret; + + sdw = dev_get_drvdata(dev); + + ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + } + + sdw_cdns_enable_interrupt(&sdw->cdns); + + return ret; +} + +#endif + +static const struct dev_pm_ops intel_pm = { + SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) +}; + static struct platform_driver sdw_intel_drv = { .probe = intel_probe, .remove = intel_remove, .driver = { .name = "int-sdw", + .pm = &intel_pm,
}, };
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+static int intel_resume(struct device *dev) +{
- struct sdw_intel *sdw;
- int ret;
- sdw = dev_get_drvdata(dev);
- ret = intel_init(sdw);
- if (ret) {
dev_err(dev, "%s failed: %d", __func__, ret);
return ret;
- }
- sdw_cdns_enable_interrupt(&sdw->cdns);
- return ret;
+}
+#endif
Suggestion: the local declaration + initialization via dev_get_drvdata() are usually combined.
Given the upstream declaration of _enable_interrupt, it does return error code/ success. Given current flow, if function gets to _enable_interrupt call, ret is already set to 0. Returning sdw_cds_enable_interrupt() directly would both simplify the definition and prevent status loss.
On 7/26/19 5:50 AM, Cezary Rojewski wrote:
On 2019-07-26 01:40, Pierre-Louis Bossart wrote:
+static int intel_resume(struct device *dev) +{ + struct sdw_intel *sdw; + int ret;
+ sdw = dev_get_drvdata(dev);
+ ret = intel_init(sdw); + if (ret) { + dev_err(dev, "%s failed: %d", __func__, ret); + return ret; + }
+ sdw_cdns_enable_interrupt(&sdw->cdns);
+ return ret; +}
+#endif
Suggestion: the local declaration + initialization via dev_get_drvdata() are usually combined.
yes, will do.
Given the upstream declaration of _enable_interrupt, it does return error code/ success. Given current flow, if function gets to _enable_interrupt call, ret is already set to 0. Returning sdw_cds_enable_interrupt() directly would both simplify the definition and prevent status loss.
sounds good, will do, thanks!
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1477c35f616f..a976480d6f36 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1161,6 +1161,12 @@ static int intel_suspend(struct device *dev)
sdw = dev_get_drvdata(dev);
+ if (sdw->cdns.bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + sdw->cdns.bus.link_id); + return 0; + } + ret = intel_link_power_down(sdw); if (ret) { dev_err(dev, "Link power down failed: %d", ret); @@ -1179,6 +1185,12 @@ static int intel_resume(struct device *dev)
sdw = dev_get_drvdata(dev);
+ if (sdw->cdns.bus.prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", + sdw->cdns.bus.link_id); + return 0; + } + ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret);
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Please add explanation why..
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1477c35f616f..a976480d6f36 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1161,6 +1161,12 @@ static int intel_suspend(struct device *dev)
sdw = dev_get_drvdata(dev);
- if (sdw->cdns.bus.prop.hw_disabled) {
dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
sdw->cdns.bus.link_id);
return 0;
- }
- ret = intel_link_power_down(sdw); if (ret) { dev_err(dev, "Link power down failed: %d", ret);
@@ -1179,6 +1185,12 @@ static int intel_resume(struct device *dev)
sdw = dev_get_drvdata(dev);
- if (sdw->cdns.bus.prop.hw_disabled) {
dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
sdw->cdns.bus.link_id);
return 0;
- }
- ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret);
-- 2.20.1
On 8/2/19 12:30 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Please add explanation why..
yes missed this
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/intel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1477c35f616f..a976480d6f36 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1161,6 +1161,12 @@ static int intel_suspend(struct device *dev)
sdw = dev_get_drvdata(dev);
- if (sdw->cdns.bus.prop.hw_disabled) {
dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
sdw->cdns.bus.link_id);
return 0;
- }
- ret = intel_link_power_down(sdw); if (ret) { dev_err(dev, "Link power down failed: %d", ret);
@@ -1179,6 +1185,12 @@ static int intel_resume(struct device *dev)
sdw = dev_get_drvdata(dev);
- if (sdw->cdns.bus.prop.hw_disabled) {
dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n",
sdw->cdns.bus.link_id);
return 0;
- }
- ret = intel_init(sdw); if (ret) { dev_err(dev, "%s failed: %d", __func__, ret);
-- 2.20.1
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 9 +++++++-- drivers/soundwire/cadence_master.h | 1 + drivers/soundwire/intel.c | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4a189e487830..f486fe15fb46 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -780,7 +780,11 @@ EXPORT_SYMBOL(sdw_cdns_thread); * init routines */
-static int do_reset(struct sdw_cdns *cdns) +/** + * sdw_cdns_exit_reset() - Program reset parameters and start bus operations + * @cdns: Cadence instance + */ +int sdw_cdns_exit_reset(struct sdw_cdns *cdns) { int ret;
@@ -804,6 +808,7 @@ static int do_reset(struct sdw_cdns *cdns)
return ret; } +EXPORT_SYMBOL(sdw_cdns_exit_reset);
/** * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config @@ -839,7 +844,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
- return do_reset(cdns); + return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index de97bc22acb7..2b551f9226f3 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -161,6 +161,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); +int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a976480d6f36..9ebe38e4d979 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1112,6 +1112,8 @@ static int intel_probe(struct platform_device *pdev)
ret = sdw_cdns_enable_interrupt(&sdw->cdns);
+ ret = sdw_cdns_exit_reset(&sdw->cdns); + /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) { @@ -1199,6 +1201,8 @@ static int intel_resume(struct device *dev)
sdw_cdns_enable_interrupt(&sdw->cdns);
+ ret = sdw_cdns_exit_reset(&sdw->cdns); + return ret; }
On Thu, Jul 25, 2019 at 06:40:27PM -0500, Pierre-Louis Bossart wrote:
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 9 +++++++-- drivers/soundwire/cadence_master.h | 1 + drivers/soundwire/intel.c | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4a189e487830..f486fe15fb46 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -780,7 +780,11 @@ EXPORT_SYMBOL(sdw_cdns_thread);
- init routines
*/
-static int do_reset(struct sdw_cdns *cdns) +/**
- sdw_cdns_exit_reset() - Program reset parameters and start bus operations
- @cdns: Cadence instance
- */
+int sdw_cdns_exit_reset(struct sdw_cdns *cdns) { int ret;
@@ -804,6 +808,7 @@ static int do_reset(struct sdw_cdns *cdns)
return ret; } +EXPORT_SYMBOL(sdw_cdns_exit_reset);
/**
- sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
@@ -839,7 +844,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
- return do_reset(cdns);
- return 0;
} EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index de97bc22acb7..2b551f9226f3 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -161,6 +161,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); +int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a976480d6f36..9ebe38e4d979 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1112,6 +1112,8 @@ static int intel_probe(struct platform_device *pdev)
ret = sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
This isn't something, that this patch changes, but if the return value above is ignored, maybe no need to assign it at all?
Thanks Guennadi
- /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) {
@@ -1199,6 +1201,8 @@ static int intel_resume(struct device *dev)
sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
- return ret;
}
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
@@ -161,6 +161,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); +int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a976480d6f36..9ebe38e4d979 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1112,6 +1112,8 @@ static int intel_probe(struct platform_device *pdev)
ret = sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
This isn't something, that this patch changes, but if the return value above is ignored, maybe no need to assign it at all?
The return of these two functions was used with in some logs at some point but they obviously vanished. I'll re-check if we care about the status, could be that it never fails Thanks!
- /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) {
@@ -1199,6 +1201,8 @@ static int intel_resume(struct device *dev)
sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
- return ret;
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Here as well
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 9 +++++++-- drivers/soundwire/cadence_master.h | 1 + drivers/soundwire/intel.c | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4a189e487830..f486fe15fb46 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -780,7 +780,11 @@ EXPORT_SYMBOL(sdw_cdns_thread);
- init routines
*/
-static int do_reset(struct sdw_cdns *cdns) +/**
- sdw_cdns_exit_reset() - Program reset parameters and start bus operations
- @cdns: Cadence instance
- */
+int sdw_cdns_exit_reset(struct sdw_cdns *cdns) { int ret;
@@ -804,6 +808,7 @@ static int do_reset(struct sdw_cdns *cdns)
return ret; } +EXPORT_SYMBOL(sdw_cdns_exit_reset);
/**
- sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
@@ -839,7 +844,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
- return do_reset(cdns);
- return 0;
} EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index de97bc22acb7..2b551f9226f3 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -161,6 +161,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); +int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a976480d6f36..9ebe38e4d979 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1112,6 +1112,8 @@ static int intel_probe(struct platform_device *pdev)
ret = sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
- /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) {
@@ -1199,6 +1201,8 @@ static int intel_resume(struct device *dev)
sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
- return ret;
}
-- 2.20.1
On 8/2/19 12:31 PM, Vinod Koul wrote:
On 25-07-19, 18:40, Pierre-Louis Bossart wrote:
Here as well
I squashed this with earlier patches to fix the init sequence in one shot
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 9 +++++++-- drivers/soundwire/cadence_master.h | 1 + drivers/soundwire/intel.c | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 4a189e487830..f486fe15fb46 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -780,7 +780,11 @@ EXPORT_SYMBOL(sdw_cdns_thread);
- init routines
*/
-static int do_reset(struct sdw_cdns *cdns) +/**
- sdw_cdns_exit_reset() - Program reset parameters and start bus operations
- @cdns: Cadence instance
- */
+int sdw_cdns_exit_reset(struct sdw_cdns *cdns) { int ret;
@@ -804,6 +808,7 @@ static int do_reset(struct sdw_cdns *cdns)
return ret; } +EXPORT_SYMBOL(sdw_cdns_exit_reset);
/**
- sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
@@ -839,7 +844,7 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
- return do_reset(cdns);
- return 0; } EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index de97bc22acb7..2b551f9226f3 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -161,6 +161,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); +int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a976480d6f36..9ebe38e4d979 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1112,6 +1112,8 @@ static int intel_probe(struct platform_device *pdev)
ret = sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
- /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) {
@@ -1199,6 +1201,8 @@ static int intel_resume(struct device *dev)
sdw_cdns_enable_interrupt(&sdw->cdns);
- ret = sdw_cdns_exit_reset(&sdw->cdns);
- return ret; }
-- 2.20.1
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 42 +++++++++++++++++------------- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 6 +++-- 3 files changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index f486fe15fb46..fa7230b0f200 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -814,33 +814,39 @@ EXPORT_SYMBOL(sdw_cdns_exit_reset); * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config * @cdns: Cadence instance */ -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) { u32 mask;
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, - CDNS_MCP_SLAVE_INTMASK0_MASK); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, - CDNS_MCP_SLAVE_INTMASK1_MASK); + if (state) { + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, + CDNS_MCP_SLAVE_INTMASK0_MASK); + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, + CDNS_MCP_SLAVE_INTMASK1_MASK);
- /* enable detection of slave state changes */ - mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT | - CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH; + /* enable detection of slave state changes */ + mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT | + CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
- /* enable detection of bus issues */ - mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | - CDNS_MCP_INT_PARITY; + /* enable detection of bus issues */ + mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | + CDNS_MCP_INT_PARITY;
- /* no detection of port interrupts for now */ + /* no detection of port interrupts for now */
- /* enable detection of RX fifo level */ - mask |= CDNS_MCP_INT_RX_WL; + /* enable detection of RX fifo level */ + mask |= CDNS_MCP_INT_RX_WL;
- /* now enable all of the above */ - mask |= CDNS_MCP_INT_IRQ; + /* now enable all of the above */ + mask |= CDNS_MCP_INT_IRQ;
- if (interrupt_mask) /* parameter override */ - mask = interrupt_mask; + if (interrupt_mask) /* parameter override */ + mask = interrupt_mask; + } else { + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, 0); + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, 0); + mask = 0; + }
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 2b551f9226f3..1a0ba36dd78f 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -162,7 +162,7 @@ int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 9ebe38e4d979..1192d5775484 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1110,7 +1110,7 @@ static int intel_probe(struct platform_device *pdev) goto err_init; }
- ret = sdw_cdns_enable_interrupt(&sdw->cdns); + ret = sdw_cdns_enable_interrupt(&sdw->cdns, true);
ret = sdw_cdns_exit_reset(&sdw->cdns);
@@ -1169,6 +1169,8 @@ static int intel_suspend(struct device *dev) return 0; }
+ sdw_cdns_enable_interrupt(&sdw->cdns, false); + ret = intel_link_power_down(sdw); if (ret) { dev_err(dev, "Link power down failed: %d", ret); @@ -1199,7 +1201,7 @@ static int intel_resume(struct device *dev) return ret; }
- sdw_cdns_enable_interrupt(&sdw->cdns); + sdw_cdns_enable_interrupt(&sdw->cdns, true);
ret = sdw_cdns_exit_reset(&sdw->cdns);
On Thu, Jul 25, 2019 at 06:40:28PM -0500, Pierre-Louis Bossart wrote:
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 42 +++++++++++++++++------------- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 6 +++-- 3 files changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index f486fe15fb46..fa7230b0f200 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -814,33 +814,39 @@ EXPORT_SYMBOL(sdw_cdns_exit_reset);
- sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config
- @cdns: Cadence instance
*/ -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) { u32 mask;
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0,
CDNS_MCP_SLAVE_INTMASK0_MASK);
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
CDNS_MCP_SLAVE_INTMASK1_MASK);
- if (state) {
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0,
CDNS_MCP_SLAVE_INTMASK0_MASK);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
CDNS_MCP_SLAVE_INTMASK1_MASK);
- /* enable detection of slave state changes */
- mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
/* enable detection of slave state changes */
mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
- /* enable detection of bus issues */
- mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
/* enable detection of bus issues */
mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
- /* no detection of port interrupts for now */
/* no detection of port interrupts for now */
- /* enable detection of RX fifo level */
- mask |= CDNS_MCP_INT_RX_WL;
/* enable detection of RX fifo level */
mask |= CDNS_MCP_INT_RX_WL;
- /* now enable all of the above */
- mask |= CDNS_MCP_INT_IRQ;
/* now enable all of the above */
mask |= CDNS_MCP_INT_IRQ;
- if (interrupt_mask) /* parameter override */
mask = interrupt_mask;
if (interrupt_mask) /* parameter override */
mask = interrupt_mask;
- } else {
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, 0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, 0);
mask = 0;
- }
Looks like this should be two functions? Especially since "state" is always a constant when it is called. If there is still a lot of common code below, maybe make it a helper function.
Thanks Guennadi
cdns_writel(cdns, CDNS_MCP_INTMASK, mask);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 2b551f9226f3..1a0ba36dd78f 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -162,7 +162,7 @@ int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state);
void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root);
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 9ebe38e4d979..1192d5775484 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1110,7 +1110,7 @@ static int intel_probe(struct platform_device *pdev) goto err_init; }
- ret = sdw_cdns_enable_interrupt(&sdw->cdns);
ret = sdw_cdns_enable_interrupt(&sdw->cdns, true);
ret = sdw_cdns_exit_reset(&sdw->cdns);
@@ -1169,6 +1169,8 @@ static int intel_suspend(struct device *dev) return 0; }
- sdw_cdns_enable_interrupt(&sdw->cdns, false);
- ret = intel_link_power_down(sdw); if (ret) { dev_err(dev, "Link power down failed: %d", ret);
@@ -1199,7 +1201,7 @@ static int intel_resume(struct device *dev) return ret; }
- sdw_cdns_enable_interrupt(&sdw->cdns);
sdw_cdns_enable_interrupt(&sdw->cdns, true);
ret = sdw_cdns_exit_reset(&sdw->cdns);
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
-int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) { u32 mask;
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0,
CDNS_MCP_SLAVE_INTMASK0_MASK);
- cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
CDNS_MCP_SLAVE_INTMASK1_MASK);
- if (state) {
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0,
CDNS_MCP_SLAVE_INTMASK0_MASK);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1,
CDNS_MCP_SLAVE_INTMASK1_MASK);
- /* enable detection of slave state changes */
- mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
/* enable detection of slave state changes */
mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT |
CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH;
- /* enable detection of bus issues */
- mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
/* enable detection of bus issues */
mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH |
CDNS_MCP_INT_PARITY;
- /* no detection of port interrupts for now */
/* no detection of port interrupts for now */
- /* enable detection of RX fifo level */
- mask |= CDNS_MCP_INT_RX_WL;
/* enable detection of RX fifo level */
mask |= CDNS_MCP_INT_RX_WL;
- /* now enable all of the above */
- mask |= CDNS_MCP_INT_IRQ;
/* now enable all of the above */
mask |= CDNS_MCP_INT_IRQ;
- if (interrupt_mask) /* parameter override */
mask = interrupt_mask;
if (interrupt_mask) /* parameter override */
mask = interrupt_mask;
- } else {
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, 0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, 0);
mask = 0;
- }
Looks like this should be two functions? Especially since "state" is always a constant when it is called. If there is still a lot of common code below, maybe make it a helper function.
Yes, the code is a bit ugly. I could initialize all the masks to zero, have the if(state) block and write the masks.
This is to kick devices into reset and see what software does
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index fa7230b0f200..53278aa2436f 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -331,6 +331,25 @@ static const struct file_operations cdns_reg_fops = { .llseek = default_llseek, };
+static int cdns_hw_reset(void *data, u64 value) +{ + struct sdw_cdns *cdns = data; + int ret; + + if (value != 1) + return 0; + + dev_info(cdns->dev, "starting link hw_reset\n"); + + ret = sdw_cdns_exit_reset(cdns); + + dev_info(cdns->dev, "link hw_reset done\n"); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); + /** * sdw_cdns_debugfs_init() - Cadence debugfs init * @cdns: Cadence instance @@ -339,6 +358,9 @@ static const struct file_operations cdns_reg_fops = { void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) { debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); + + debugfs_create_file_unsafe("cdns-hw-reset", 0200, root, cdns, + &cdns_hw_reset_fops); } EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
On Thu, Jul 25, 2019 at 06:40:29PM -0500, Pierre-Louis Bossart wrote:
This is to kick devices into reset and see what software does
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index fa7230b0f200..53278aa2436f 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -331,6 +331,25 @@ static const struct file_operations cdns_reg_fops = { .llseek = default_llseek, };
+static int cdns_hw_reset(void *data, u64 value) +{
- struct sdw_cdns *cdns = data;
- int ret;
- if (value != 1)
return 0;
- dev_info(cdns->dev, "starting link hw_reset\n");
- ret = sdw_cdns_exit_reset(cdns);
- dev_info(cdns->dev, "link hw_reset done\n");
Do not be noisy for when things always go right. This looks like debuggging code, please remove.
- return ret;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n");
/**
- sdw_cdns_debugfs_init() - Cadence debugfs init
- @cdns: Cadence instance
@@ -339,6 +358,9 @@ static const struct file_operations cdns_reg_fops = { void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) { debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
- debugfs_create_file_unsafe("cdns-hw-reset", 0200, root, cdns,
&cdns_hw_reset_fops);
Why unsafe?
thanks,
greg k-h
Thanks for the review Greg.
+static int cdns_hw_reset(void *data, u64 value) +{
- struct sdw_cdns *cdns = data;
- int ret;
- if (value != 1)
return 0;
- dev_info(cdns->dev, "starting link hw_reset\n");
- ret = sdw_cdns_exit_reset(cdns);
- dev_info(cdns->dev, "link hw_reset done\n");
Do not be noisy for when things always go right. This looks like debuggging code, please remove.
yes, missed this in the cleanup, will remove.
+DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n");
- /**
- sdw_cdns_debugfs_init() - Cadence debugfs init
- @cdns: Cadence instance
@@ -339,6 +358,9 @@ static const struct file_operations cdns_reg_fops = { void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) { debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
- debugfs_create_file_unsafe("cdns-hw-reset", 0200, root, cdns,
&cdns_hw_reset_fops);
Why unsafe?
Dunno. I followed the documentation and my take-away, along with a number of examples, was to use _unsafe. I really have no idea if this is correct or not, I can remove this qualifier if that's not needed.
On Thu, Jul 25, 2019 at 06:40:29PM -0500, Pierre-Louis Bossart wrote:
This is to kick devices into reset and see what software does
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index fa7230b0f200..53278aa2436f 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -331,6 +331,25 @@ static const struct file_operations cdns_reg_fops = { .llseek = default_llseek, };
+static int cdns_hw_reset(void *data, u64 value) +{
- struct sdw_cdns *cdns = data;
- int ret;
- if (value != 1)
return 0;
- dev_info(cdns->dev, "starting link hw_reset\n");
- ret = sdw_cdns_exit_reset(cdns);
- dev_info(cdns->dev, "link hw_reset done\n");
Both really should be dev_info()? Maybe at least one of them can be dev_dbg()?
Thanks Guennadi
- return ret;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n");
/**
- sdw_cdns_debugfs_init() - Cadence debugfs init
- @cdns: Cadence instance
@@ -339,6 +358,9 @@ static const struct file_operations cdns_reg_fops = { void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) { debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops);
- debugfs_create_file_unsafe("cdns-hw-reset", 0200, root, cdns,
&cdns_hw_reset_fops);
} EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init);
-- 2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
+static int cdns_hw_reset(void *data, u64 value) +{
- struct sdw_cdns *cdns = data;
- int ret;
- if (value != 1)
return 0;
- dev_info(cdns->dev, "starting link hw_reset\n");
- ret = sdw_cdns_exit_reset(cdns);
- dev_info(cdns->dev, "link hw_reset done\n");
Both really should be dev_info()? Maybe at least one of them can be dev_dbg()?
I have to walk back on what I explained to Greg. The idea was to have a dmesg trace when this function as called when the user plays with debugfs, otherwise the dmesg log is difficult to interpret (devices can go off the bus on their own). I'll keep the first one only and demote it to dev_dbg.
The use of clock stop is not a requirement, the IP can e.g. be completely power gated and not detect any wakes while in s2idle/deep sleep.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/cadence_master.c | 15 ++++++++------- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 53278aa2436f..4ab6f70d7705 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1010,7 +1010,7 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols) * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance */ -int sdw_cdns_init(struct sdw_cdns *cdns) +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit) { struct sdw_bus *bus = &cdns->bus; struct sdw_master_prop *prop = &bus->prop; @@ -1018,12 +1018,13 @@ int sdw_cdns_init(struct sdw_cdns *cdns) int divider; int ret;
- /* Exit clock stop */ - ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CLK_STOP_CLR); - if (ret < 0) { - dev_err(cdns->dev, "Couldn't exit from clock stop\n"); - return ret; + if (clock_stop_exit) { + ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_CLK_STOP_CLR); + if (ret < 0) { + dev_err(cdns->dev, "Couldn't exit from clock stop\n"); + return ret; + } }
/* Set clock divider */ diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 1a0ba36dd78f..091b771b570d 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -158,7 +158,7 @@ extern struct sdw_master_ops sdw_cdns_master_ops; irqreturn_t sdw_cdns_irq(int irq, void *dev_id); irqreturn_t sdw_cdns_thread(int irq, void *dev_id);
-int sdw_cdns_init(struct sdw_cdns *cdns); +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1192d5775484..db7bf2912767 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1043,7 +1043,7 @@ static int intel_init(struct sdw_intel *sdw) intel_link_power_up(sdw); intel_shim_init(sdw);
- return sdw_cdns_init(&sdw->cdns); + return sdw_cdns_init(&sdw->cdns, false); }
/*
On Thu, Jul 25, 2019 at 06:40:30PM -0500, Pierre-Louis Bossart wrote:
The use of clock stop is not a requirement, the IP can e.g. be completely power gated and not detect any wakes while in s2idle/deep sleep.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
drivers/soundwire/cadence_master.c | 15 ++++++++------- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 53278aa2436f..4ab6f70d7705 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1010,7 +1010,7 @@ static u32 cdns_set_default_frame_shape(int n_rows, int n_cols)
- sdw_cdns_init() - Cadence initialization
- @cdns: Cadence instance
*/ -int sdw_cdns_init(struct sdw_cdns *cdns) +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit) { struct sdw_bus *bus = &cdns->bus; struct sdw_master_prop *prop = &bus->prop; @@ -1018,12 +1018,13 @@ int sdw_cdns_init(struct sdw_cdns *cdns) int divider; int ret;
- /* Exit clock stop */
- ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_CLK_STOP_CLR);
- if (ret < 0) {
dev_err(cdns->dev, "Couldn't exit from clock stop\n");
return ret;
if (clock_stop_exit) {
ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL,
CDNS_MCP_CONTROL_CLK_STOP_CLR);
if (ret < 0) {
dev_err(cdns->dev, "Couldn't exit from clock stop\n");
return ret;
}
}
/* Set clock divider */
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 1a0ba36dd78f..091b771b570d 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -158,7 +158,7 @@ extern struct sdw_master_ops sdw_cdns_master_ops; irqreturn_t sdw_cdns_irq(int irq, void *dev_id); irqreturn_t sdw_cdns_thread(int irq, void *dev_id);
-int sdw_cdns_init(struct sdw_cdns *cdns); +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1192d5775484..db7bf2912767 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1043,7 +1043,7 @@ static int intel_init(struct sdw_intel *sdw) intel_link_power_up(sdw); intel_shim_init(sdw);
- return sdw_cdns_init(&sdw->cdns);
- return sdw_cdns_init(&sdw->cdns, false);
This is the only caller of this function so far, so, it looks like the second argument ATM is always "false." I assume you foresee other uses with "true" in the future, otherwise maybe just fix it to false in the function?
Thanks Guennadi
}
/*
2.20.1
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
-int sdw_cdns_init(struct sdw_cdns *cdns); +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1192d5775484..db7bf2912767 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1043,7 +1043,7 @@ static int intel_init(struct sdw_intel *sdw) intel_link_power_up(sdw); intel_shim_init(sdw);
- return sdw_cdns_init(&sdw->cdns);
- return sdw_cdns_init(&sdw->cdns, false);
This is the only caller of this function so far, so, it looks like the second argument ATM is always "false." I assume you foresee other uses with "true" in the future, otherwise maybe just fix it to false in the function?
Since we are at RFC level things are not set in stone, it's not fully clear if this test is required or not. I added it since Rander reported an error on one of the target platforms that I didn't see personally. We'll double-check if it's needed. And if indeed it's needed, yes it'll be set to true when we add clock stop.
Add basic hooks in DAI .startup and .shutdown callbacks. The SoundWire IP should be powered between those two calls.
By default the platform_device is in SUSPENDED mode, it is required to call pm_runtime_set_active() before _enable()
FIXME: do we need to use mark_last_busy()?
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index db7bf2912767..1394a2322553 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -707,6 +707,23 @@ static void intel_port_cleanup(struct sdw_cdns_dma_data *dma) } }
+static int intel_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = pm_runtime_get_sync(cdns->dev); + if (ret < 0) { + dev_err_ratelimited(cdns->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(cdns->dev); + } + + return ret; +} + static int intel_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -829,6 +846,8 @@ void intel_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_cdns_dma_data *dma; + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + int ret;
dma = snd_soc_dai_get_dma_data(dai, substream); if (!dma) @@ -836,6 +855,13 @@ void intel_shutdown(struct snd_pcm_substream *substream,
snd_soc_dai_set_dma_data(dai, substream, NULL); kfree(dma); + + pm_runtime_mark_last_busy(cdns->dev); + ret = pm_runtime_put_autosuspend(cdns->dev); + if (ret < 0) + dev_err_ratelimited(cdns->dev, + "pM_runtime_put_autosuspend failed in %s:, ret %d\n", + __func__, ret); }
static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, @@ -851,6 +877,7 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, }
static const struct snd_soc_dai_ops intel_pcm_dai_ops = { + .startup = intel_startup, .hw_params = intel_hw_params, .hw_free = intel_hw_free, .shutdown = intel_shutdown, @@ -1124,6 +1151,15 @@ static int intel_probe(struct platform_device *pdev)
intel_debugfs_init(sdw);
+ /* Enable PM */ + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + + pm_runtime_mark_last_busy(&pdev->dev); /* FIXME: needed? */ + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0;
err_dai: @@ -1141,6 +1177,7 @@ static int intel_remove(struct platform_device *pdev) sdw = platform_get_drvdata(pdev);
if (!sdw->cdns.bus.prop.hw_disabled) { + pm_runtime_disable(&pdev->dev); intel_debugfs_exit(sdw); free_irq(sdw->res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); @@ -1212,6 +1249,7 @@ static int intel_resume(struct device *dev)
static const struct dev_pm_ops intel_pm = { SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) + SET_RUNTIME_PM_OPS(intel_suspend, intel_resume, NULL) };
static struct platform_driver sdw_intel_drv = {
We have a conceptual issue on restart: the interaction with the slaves can start before (re) enumeration is complete. Add a delay for now but we will need to have an async notification that all devices are back on the bus.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- drivers/soundwire/intel.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 1394a2322553..e4ea430b5a8e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1242,6 +1242,11 @@ static int intel_resume(struct device *dev)
ret = sdw_cdns_exit_reset(&sdw->cdns);
+ /* add delay to let Slaves re-enumerate */ + usleep_range(20000, 30000); + + dev_dbg(dev, "%s done\n", __func__); + return ret; }
On 2019-07-26 01:39, Pierre-Louis Bossart wrote:
The existing upstream code allows for SoundWire devices to be enumerated and managed by the bus, but streaming is not currently supported.
Bard Liao, Rander Wang and I did quite a bit of integration/validation work to close this gap and we now have SoundWire streaming + basic power managemement on Intel CometLake and IceLake reference boards. These changes are still preliminary and should not be merged as is, but it's time to start reviews. While the number of patches is quite large, each of the changes is quite small.
SOF driver changes will be submitted shortly as well but are still being validated.
ClockStop modes and synchronized playback on multiple links are not supported for now and will likely be part of the next cycle (dependencies on codec drivers and multi-cpu DAI support).
Acknowledgements: This work would not have been possible without the support of Slawomir Blauciak and Tomasz Lauda on the SOF side, currently being reviewed, see https://github.com/thesofproject/sof/pull/1638
Comments and feedback welcome!
Hello Pierre,
This patchset is pretty large - I'd suggest dividing next RFC into segments: debugfs, info, power-management, basic flow corrections and frame shape calculator. Some commits have no messages and others lack additional info - tried to provide feedback wherever I could, though, especially for the last one, it would be vital to post additional info so in-depth feedback can be provided.
Maybe nothing for calculator will come up, maybe something will. In general I remember it being an essential part of SDW and one where many bugs where found during the initial verification phase.
Thanks for your contribution and have a good day! Czarek
Comments and feedback welcome!
Hello Pierre,
This patchset is pretty large - I'd suggest dividing next RFC into segments: debugfs, info, power-management, basic flow corrections and frame shape calculator.
There was an intent to provide a logical progression...
First debugfs, since I believe it was reviewed before, and I wanted folks like Greg to double-check it without burrying it too deep.
Then all corrections, followed by the allocator.
And last all PM stuff, split by regular suspend/resume and pm_runtime.
The RFC state is precisely to gather feedback, if folks want a different order that's fine. I just wanted to be transparent and share what we have.
Some commits have no messages and others lack additional info - tried to provide feedback wherever I could, though, especially for the last one, it would be vital to post additional info so in-depth feedback can be provided.
The lack of commits is a miss, I went from 170 debug/integration patches to 40 yesterday and my brain was fried.
Maybe nothing for calculator will come up, maybe something will. In general I remember it being an essential part of SDW and one where many bugs where found during the initial verification phase.
the frame allocation is a critical piece and it does need to be hardened. However we can do so in steps. The current setups we have support 1 Slave per link and a limited amount of bandwidth. The links themselves don't operate at the max frequency. Also note that that the streams are 'statically' defined by the dailinks, and the allocation is not fully dynamic with random configurations being request. If you fail you fail fast.
Nevertheless I do plan to recheck the allocator with an additional scripting tool. It'd be very good to e.g. dump the current setup in a debugfs file and show to users what is happening (or not happening). I didn't recall you worked on SoundWire and I can use your practical knowledge to make the code and tools better :-)
Thanks for your contribution and have a good day!
Thanks for reviewing this long series and have a nice week-end. -Pierre
participants (10)
-
Andy Shevchenko
-
Bard liao
-
Cezary Rojewski
-
Greg KH
-
Guennadi Liakhovetski
-
Jan Kotas
-
Pierre-Louis Bossart
-
rander.wang
-
Sanyog Kale
-
Vinod Koul