soundwire updates for 5.20-rc1

- Core: solve the driver bind/unbind problem and remove ops pointer
  - intel: runtime pm updates
  - qcom: audio clock gating updates and device status checks
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmLQ8OEACgkQfBQHDyUj
 g0efIxAAiU+f6Z75b+NznPvX3/OtBuZoA0kx80f0QUmiZ83GLbSIAJuvaYBBw7+x
 WkihiBg85VtymAPSe+8BRPhpy5QzQp0J2w1ZcyHfBQrgOen6kBEvGMkGbpDE+/Ym
 ipwCSqG6XeVcbdjKKHXOP7Pr4yUjm5QoFcbZmorElvMVBrCHFPrEcdbzB4h92eb7
 3gQjSM2hyMrszAbY48qlesNNjYeaVpG6U8r0dVgiWg5mr5HrkusIel9KyV+HChho
 z3cwjqQd4HSNJUgj0A7bVGE+j3tUxFf95w0qIG0VRs6q8baTFxKVfjb9cd6i6/sT
 hTSpim7/040GU6CabS0WHgMfI7D3besYLlaWbcLjSe1Vam/mJID9zw7S7hO5hRGj
 uZ2nKeOsHPf9bcdE3e6BXkpMEv9rImuXrwwHbu7OmAeFDSdT9PpSvThxX0qobwL8
 wqVfBkoTs9ovnbQ28JSnQkys1r1Q3Nwt2LJ/SYvyEbRmxfAJvIRqgwwq25nGkJIS
 KasTDvMqamYlaBirH5kBAvsOagyQP8wHq1XFWSLj82sqzeesbQEtxotxiyU1iGcj
 kO1xdE/XchsHItFy8UADxRGcZ4mmHxdP9nYKk6ZR9h1bnQLZxqQylE8aGuF+C9LG
 rAgh4tZx0guJwxdcsviuzdI8xxJZLeoVPJNOB4z5AC7UtoC9E8A=
 =n6b4
 -----END PGP SIGNATURE-----

Merge tag 'soundwire-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next

Vinod writes:
 "soundwire updates for 5.20-rc1

   - Core: solve the driver bind/unbind problem and remove ops pointer
   - intel: runtime pm updates
   - qcom: audio clock gating updates and device status checks"

* tag 'soundwire-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: qcom: Enable software clock gating requirement flag
  soundwire: qcom: Check device status before reading devid
  soundwire: qcom: Add flag for software clock gating check
  soundwire: qcom: Add support for controlling audio CGCR from HLOS
  soundwire: intel: use pm_runtime_resume() on component probe
  soundwire: peripheral: remove useless ops pointer
  soundwire: revisit driver bind/unbind and callbacks
  soundwire: bus_type: fix remove and shutdown support
This commit is contained in:
Greg Kroah-Hartman 2022-07-15 07:31:59 +02:00
commit 2306137b13
7 changed files with 156 additions and 69 deletions

View File

@ -7,6 +7,7 @@
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "bus.h"
#include "sysfs_local.h"
@ -842,15 +843,21 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave,
enum sdw_clk_stop_mode mode,
enum sdw_clk_stop_type type)
{
int ret;
int ret = 0;
if (slave->ops && slave->ops->clk_stop) {
ret = slave->ops->clk_stop(slave, mode, type);
if (ret < 0)
return ret;
mutex_lock(&slave->sdw_dev_lock);
if (slave->probed) {
struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (drv->ops && drv->ops->clk_stop)
ret = drv->ops->clk_stop(slave, mode, type);
}
return 0;
mutex_unlock(&slave->sdw_dev_lock);
return ret;
}
static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave,
@ -1611,14 +1618,24 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
}
/* Update the Slave driver */
if (slave_notify && slave->ops &&
slave->ops->interrupt_callback) {
slave_intr.sdca_cascade = sdca_cascade;
slave_intr.control_port = clear;
memcpy(slave_intr.port, &port_status,
sizeof(slave_intr.port));
if (slave_notify) {
mutex_lock(&slave->sdw_dev_lock);
slave->ops->interrupt_callback(slave, &slave_intr);
if (slave->probed) {
struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (drv->ops && drv->ops->interrupt_callback) {
slave_intr.sdca_cascade = sdca_cascade;
slave_intr.control_port = clear;
memcpy(slave_intr.port, &port_status,
sizeof(slave_intr.port));
drv->ops->interrupt_callback(slave, &slave_intr);
}
}
mutex_unlock(&slave->sdw_dev_lock);
}
/* Ack interrupt */
@ -1692,29 +1709,21 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
static int sdw_update_slave_status(struct sdw_slave *slave,
enum sdw_slave_status status)
{
unsigned long time;
int ret = 0;
if (!slave->probed) {
/*
* the slave status update is typically handled in an
* interrupt thread, which can race with the driver
* probe, e.g. when a module needs to be loaded.
*
* make sure the probe is complete before updating
* status.
*/
time = wait_for_completion_timeout(&slave->probe_complete,
msecs_to_jiffies(DEFAULT_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Probe not complete, timed out\n");
return -ETIMEDOUT;
}
mutex_lock(&slave->sdw_dev_lock);
if (slave->probed) {
struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (drv->ops && drv->ops->update_status)
ret = drv->ops->update_status(slave, status);
}
if (!slave->ops || !slave->ops->update_status)
return 0;
mutex_unlock(&slave->sdw_dev_lock);
return slave->ops->update_status(slave, status);
return ret;
}
/**

View File

@ -98,8 +98,6 @@ static int sdw_drv_probe(struct device *dev)
if (!id)
return -ENODEV;
slave->ops = drv->ops;
/*
* attach to power domain but don't turn on (last arg)
*/
@ -107,19 +105,23 @@ static int sdw_drv_probe(struct device *dev)
if (ret)
return ret;
mutex_lock(&slave->sdw_dev_lock);
ret = drv->probe(slave, id);
if (ret) {
name = drv->name;
if (!name)
name = drv->driver.name;
mutex_unlock(&slave->sdw_dev_lock);
dev_err(dev, "Probe of %s failed: %d\n", name, ret);
dev_pm_domain_detach(dev, false);
return ret;
}
/* device is probed so let's read the properties now */
if (slave->ops && slave->ops->read_prop)
slave->ops->read_prop(slave);
if (drv->ops && drv->ops->read_prop)
drv->ops->read_prop(slave);
/* init the sysfs as we have properties now */
ret = sdw_slave_sysfs_init(slave);
@ -139,7 +141,19 @@ static int sdw_drv_probe(struct device *dev)
slave->prop.clk_stop_timeout);
slave->probed = true;
complete(&slave->probe_complete);
/*
* if the probe happened after the bus was started, notify the codec driver
* of the current hardware status to e.g. start the initialization.
* Errors are only logged as warnings to avoid failing the probe.
*/
if (drv->ops && drv->ops->update_status) {
ret = drv->ops->update_status(slave, slave->status);
if (ret < 0)
dev_warn(dev, "%s: update_status failed with status %d\n", __func__, ret);
}
mutex_unlock(&slave->sdw_dev_lock);
dev_dbg(dev, "probe complete\n");
@ -152,9 +166,15 @@ static int sdw_drv_remove(struct device *dev)
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
int ret = 0;
mutex_lock(&slave->sdw_dev_lock);
slave->probed = false;
if (drv->remove)
ret = drv->remove(slave);
mutex_unlock(&slave->sdw_dev_lock);
dev_pm_domain_detach(dev, false);
return ret;
@ -193,12 +213,8 @@ int __sdw_register_driver(struct sdw_driver *drv, struct module *owner)
drv->driver.owner = owner;
drv->driver.probe = sdw_drv_probe;
if (drv->remove)
drv->driver.remove = sdw_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = sdw_drv_shutdown;
drv->driver.remove = sdw_drv_remove;
drv->driver.shutdown = sdw_drv_shutdown;
return driver_register(&drv->driver);
}

View File

@ -1043,6 +1043,23 @@ static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct sn
return ret;
}
static int intel_component_probe(struct snd_soc_component *component)
{
int ret;
/*
* make sure the device is pm_runtime_active before initiating
* bus transactions during the card registration.
* We use pm_runtime_resume() here, without taking a reference
* and releasing it immediately.
*/
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
return 0;
}
static int intel_component_dais_suspend(struct snd_soc_component *component)
{
struct snd_soc_dai *dai;
@ -1098,6 +1115,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
static const struct snd_soc_component_driver dai_component = {
.name = "soundwire",
.probe = intel_component_probe,
.suspend = intel_component_dais_suspend
};

View File

@ -13,6 +13,7 @@
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/pm_wakeirq.h>
#include <linux/slimbus.h>
@ -142,6 +143,7 @@ struct qcom_swrm_ctrl {
struct device *dev;
struct regmap *regmap;
void __iomem *mmio;
struct reset_control *audio_cgcr;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
@ -179,6 +181,7 @@ struct qcom_swrm_ctrl {
struct qcom_swrm_data {
u32 default_cols;
u32 default_rows;
bool sw_clk_gate_required;
};
static const struct qcom_swrm_data swrm_v1_3_data = {
@ -191,6 +194,12 @@ static const struct qcom_swrm_data swrm_v1_5_data = {
.default_cols = 16,
};
static const struct qcom_swrm_data swrm_v1_6_data = {
.default_rows = 50,
.default_cols = 16,
.sw_clk_gate_required = true,
};
#define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus)
static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg,
@ -471,6 +480,10 @@ static int qcom_swrm_enumerate(struct sdw_bus *bus)
char *buf1 = (char *)&val1, *buf2 = (char *)&val2;
for (i = 1; i <= SDW_MAX_DEVICES; i++) {
/* do not continue if the status is Not Present */
if (!ctrl->status[i])
continue;
/*SCP_Devid5 - Devid 4*/
ctrl->reg_read(ctrl, SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i), &val1);
@ -656,6 +669,8 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
val = FIELD_PREP(SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK, ctrl->rows_index);
val |= FIELD_PREP(SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK, ctrl->cols_index);
reset_control_reset(ctrl->audio_cgcr);
ctrl->reg_write(ctrl, SWRM_MCP_FRAME_CTRL_BANK_ADDR(0), val);
/* Enable Auto enumeration */
@ -1307,6 +1322,15 @@ static int qcom_swrm_probe(struct platform_device *pdev)
return PTR_ERR(ctrl->mmio);
}
if (data->sw_clk_gate_required) {
ctrl->audio_cgcr = devm_reset_control_get_exclusive(dev, "swr_audio_cgcr");
if (IS_ERR_OR_NULL(ctrl->audio_cgcr)) {
dev_err(dev, "Failed to get cgcr reset ctrl required for SW gating\n");
ret = PTR_ERR(ctrl->audio_cgcr);
goto err_init;
}
}
ctrl->irq = of_irq_get(dev->of_node, 0);
if (ctrl->irq < 0) {
ret = ctrl->irq;
@ -1332,6 +1356,10 @@ static int qcom_swrm_probe(struct platform_device *pdev)
ctrl->bus.compute_params = &qcom_swrm_compute_params;
ctrl->bus.clk_stop_timeout = 300;
ctrl->audio_cgcr = devm_reset_control_get_exclusive(dev, "swr_audio_cgcr");
if (IS_ERR(ctrl->audio_cgcr))
dev_err(dev, "Failed to get audio_cgcr reset required for soundwire-v1.6.0\n");
ret = qcom_swrm_get_port_config(ctrl);
if (ret)
goto err_clk;
@ -1485,6 +1513,8 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev)
qcom_swrm_get_device_status(ctrl);
sdw_handle_slave_status(&ctrl->bus, ctrl->status);
} else {
reset_control_reset(ctrl->audio_cgcr);
ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL, SWRM_MCP_BUS_CLK_START);
ctrl->reg_write(ctrl, SWRM_INTERRUPT_CLEAR,
SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET);
@ -1548,7 +1578,7 @@ static const struct dev_pm_ops swrm_dev_pm_ops = {
static const struct of_device_id qcom_swrm_of_match[] = {
{ .compatible = "qcom,soundwire-v1.3.0", .data = &swrm_v1_3_data },
{ .compatible = "qcom,soundwire-v1.5.1", .data = &swrm_v1_5_data },
{ .compatible = "qcom,soundwire-v1.6.0", .data = &swrm_v1_5_data },
{ .compatible = "qcom,soundwire-v1.6.0", .data = &swrm_v1_6_data },
{/* sentinel */},
};

View File

@ -12,6 +12,7 @@ static void sdw_slave_release(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
mutex_destroy(&slave->sdw_dev_lock);
kfree(slave);
}
@ -58,9 +59,9 @@ int sdw_slave_add(struct sdw_bus *bus,
init_completion(&slave->enumeration_complete);
init_completion(&slave->initialization_complete);
slave->dev_num = 0;
init_completion(&slave->probe_complete);
slave->probed = false;
slave->first_interrupt_done = false;
mutex_init(&slave->sdw_dev_lock);
for (i = 0; i < SDW_MAX_PORTS; i++)
init_completion(&slave->port_ready[i]);

View File

@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/soc.h>
#include "bus.h"
@ -401,20 +402,26 @@ static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt,
struct sdw_prepare_ch prep_ch,
enum sdw_port_prep_ops cmd)
{
const struct sdw_slave_ops *ops = s_rt->slave->ops;
int ret;
int ret = 0;
struct sdw_slave *slave = s_rt->slave;
if (ops->port_prep) {
ret = ops->port_prep(s_rt->slave, &prep_ch, cmd);
if (ret < 0) {
dev_err(&s_rt->slave->dev,
"Slave Port Prep cmd %d failed: %d\n",
cmd, ret);
return ret;
mutex_lock(&slave->sdw_dev_lock);
if (slave->probed) {
struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (drv->ops && drv->ops->port_prep) {
ret = drv->ops->port_prep(slave, &prep_ch, cmd);
if (ret < 0)
dev_err(dev, "Slave Port Prep cmd %d failed: %d\n",
cmd, ret);
}
}
return 0;
mutex_unlock(&slave->sdw_dev_lock);
return ret;
}
static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
@ -578,7 +585,7 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt)
struct sdw_slave_runtime *s_rt;
struct sdw_bus *bus = m_rt->bus;
struct sdw_slave *slave;
int ret = 0;
int ret;
if (bus->ops->set_bus_conf) {
ret = bus->ops->set_bus_conf(bus, &bus->params);
@ -589,17 +596,27 @@ static int sdw_notify_config(struct sdw_master_runtime *m_rt)
list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
slave = s_rt->slave;
if (slave->ops->bus_config) {
ret = slave->ops->bus_config(slave, &bus->params);
if (ret < 0) {
dev_err(bus->dev, "Notify Slave: %d failed\n",
slave->dev_num);
return ret;
mutex_lock(&slave->sdw_dev_lock);
if (slave->probed) {
struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (drv->ops && drv->ops->bus_config) {
ret = drv->ops->bus_config(slave, &bus->params);
if (ret < 0) {
dev_err(dev, "Notify Slave: %d failed\n",
slave->dev_num);
mutex_unlock(&slave->sdw_dev_lock);
return ret;
}
}
}
mutex_unlock(&slave->sdw_dev_lock);
}
return ret;
return 0;
}
/**

View File

@ -637,7 +637,6 @@ struct sdw_slave_ops {
* @dev: Linux device
* @status: Status reported by the Slave
* @bus: Bus handle
* @ops: Slave callback ops
* @prop: Slave properties
* @debugfs: Slave debugfs
* @node: node for bus list
@ -646,9 +645,6 @@ struct sdw_slave_ops {
* @dev_num: Current Device Number, values can be 0 or dev_num_sticky
* @dev_num_sticky: one-time static Device Number assigned by Bus
* @probed: boolean tracking driver state
* @probe_complete: completion utility to control potential races
* on startup between driver probe/initialization and SoundWire
* Slave state changes/implementation-defined interrupts
* @enumeration_complete: completion utility to control potential races
* on startup between device enumeration and read/write access to the
* Slave device
@ -663,13 +659,13 @@ struct sdw_slave_ops {
* for a Slave happens for the first time after enumeration
* @is_mockup_device: status flag used to squelch errors in the command/control
* protocol for SoundWire mockup devices
* @sdw_dev_lock: mutex used to protect callbacks/remove races
*/
struct sdw_slave {
struct sdw_slave_id id;
struct device dev;
enum sdw_slave_status status;
struct sdw_bus *bus;
const struct sdw_slave_ops *ops;
struct sdw_slave_prop prop;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
@ -680,12 +676,12 @@ struct sdw_slave {
u16 dev_num;
u16 dev_num_sticky;
bool probed;
struct completion probe_complete;
struct completion enumeration_complete;
struct completion initialization_complete;
u32 unattach_request;
bool first_interrupt_done;
bool is_mockup_device;
struct mutex sdw_dev_lock; /* protect callbacks/remove races */
};
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)