Commit 6e32dfcc authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull soundwire updates from Vinod Koul:
 "Device numbering and intel driver changes are main features:

   - Core support for soundwire device number allocation

   - intel driver updates for adding hw_params for DAI ops, hybrid
     number allocation and power managemnt callback updates

   - DT header include changes for subsystem"

* tag 'soundwire-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: intel_ace2x: add DAI hw_params/prepare/hw_free callbacks
  soundwire: intel_auxdevice: add hybrid IDA-based device_number allocation
  soundwire: bus: add callbacks for device_number allocation
  soundwire: extend parameters of new_peripheral_assigned() callback
  soundWire: intel_auxdevice: resume 'sdw-master' on startup and system resume
  soundwire: intel_auxdevice: enable pm_runtime earlier on startup
  soundwire: Explicitly include correct DT includes
parents bac8a20f 8c4c9a9a
Loading
Loading
Loading
Loading
+8 −10
Original line number Diff line number Diff line
@@ -13,7 +13,6 @@
#include "sysfs_local.h"

static DEFINE_IDA(sdw_bus_ida);
static DEFINE_IDA(sdw_peripheral_ida);

static int sdw_get_id(struct sdw_bus *bus)
{
@@ -194,8 +193,8 @@ static int sdw_delete_slave(struct device *dev, void *data)

	if (slave->dev_num) { /* clear dev_num if assigned */
		clear_bit(slave->dev_num, bus->assigned);
		if (bus->dev_num_ida_min)
			ida_free(&sdw_peripheral_ida, slave->dev_num);
		if (bus->ops && bus->ops->put_device_num)
			bus->ops->put_device_num(bus, slave);
	}
	list_del_init(&slave->node);
	mutex_unlock(&bus->bus_lock);
@@ -739,16 +738,15 @@ EXPORT_SYMBOL(sdw_compare_devid);
/* called with bus_lock held */
static int sdw_get_device_num(struct sdw_slave *slave)
{
	struct sdw_bus *bus = slave->bus;
	int bit;

	if (slave->bus->dev_num_ida_min) {
		bit = ida_alloc_range(&sdw_peripheral_ida,
				      slave->bus->dev_num_ida_min, SDW_MAX_DEVICES,
				      GFP_KERNEL);
	if (bus->ops && bus->ops->get_device_num) {
		bit = bus->ops->get_device_num(bus, slave);
		if (bit < 0)
			goto err;
	} else {
		bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES);
		bit = find_first_zero_bit(bus->assigned, SDW_MAX_DEVICES);
		if (bit == SDW_MAX_DEVICES) {
			bit = -ENODEV;
			goto err;
@@ -759,7 +757,7 @@ static int sdw_get_device_num(struct sdw_slave *slave)
	 * Do not update dev_num in Slave data structure here,
	 * Update once program dev_num is successful
	 */
	set_bit(bit, slave->bus->assigned);
	set_bit(bit, bus->assigned);

err:
	return bit;
@@ -810,7 +808,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
	slave->dev_num = slave->dev_num_sticky;

	if (bus->ops && bus->ops->new_peripheral_assigned)
		bus->ops->new_peripheral_assigned(bus, dev_num);
		bus->ops->new_peripheral_assigned(bus, slave, dev_num);

	return 0;
}
+283 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
#include <sound/pcm_params.h>
#include <sound/hda-mlink.h>
#include "cadence_master.h"
#include "bus.h"
@@ -191,10 +192,292 @@ static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw)
	return hdac_bus_eml_sdw_check_cmdsync_unlocked(sdw->link_res->hbus);
}

/* DAI callbacks */
static int intel_params_stream(struct sdw_intel *sdw,
			       struct snd_pcm_substream *substream,
			       struct snd_soc_dai *dai,
			       struct snd_pcm_hw_params *hw_params,
			       int link_id, int alh_stream_id)
{
	struct sdw_intel_link_res *res = sdw->link_res;
	struct sdw_intel_stream_params_data params_data;

	params_data.substream = substream;
	params_data.dai = dai;
	params_data.hw_params = hw_params;
	params_data.link_id = link_id;
	params_data.alh_stream_id = alh_stream_id;

	if (res->ops && res->ops->params_stream && res->dev)
		return res->ops->params_stream(res->dev,
					       &params_data);
	return -EIO;
}

static int intel_free_stream(struct sdw_intel *sdw,
			     struct snd_pcm_substream *substream,
			     struct snd_soc_dai *dai,
			     int link_id)

{
	struct sdw_intel_link_res *res = sdw->link_res;
	struct sdw_intel_stream_free_data free_data;

	free_data.substream = substream;
	free_data.dai = dai;
	free_data.link_id = link_id;

	if (res->ops && res->ops->free_stream && res->dev)
		return res->ops->free_stream(res->dev,
					     &free_data);

	return 0;
}

/*
 * DAI operations
 */
static int intel_hw_params(struct snd_pcm_substream *substream,
			   struct snd_pcm_hw_params *params,
			   struct snd_soc_dai *dai)
{
	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	struct sdw_cdns_dai_runtime *dai_runtime;
	struct sdw_cdns_pdi *pdi;
	struct sdw_stream_config sconfig;
	struct sdw_port_config *pconfig;
	int ch, dir;
	int ret;

	dai_runtime = cdns->dai_runtime_array[dai->id];
	if (!dai_runtime)
		return -EIO;

	ch = params_channels(params);
	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
		dir = SDW_DATA_DIR_RX;
	else
		dir = SDW_DATA_DIR_TX;

	pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id);

	if (!pdi) {
		ret = -EINVAL;
		goto error;
	}

	/* the SHIM will be configured in the callback functions */

	sdw_cdns_config_stream(cdns, ch, dir, pdi);

	/* store pdi and state, may be needed in prepare step */
	dai_runtime->paused = false;
	dai_runtime->suspended = false;
	dai_runtime->pdi = pdi;

	/* Inform DSP about PDI stream number */
	ret = intel_params_stream(sdw, substream, dai, params,
				  sdw->instance,
				  pdi->intel_alh_id);
	if (ret)
		goto error;

	sconfig.direction = dir;
	sconfig.ch_count = ch;
	sconfig.frame_rate = params_rate(params);
	sconfig.type = dai_runtime->stream_type;

	sconfig.bps = snd_pcm_format_width(params_format(params));

	/* Port configuration */
	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
	if (!pconfig) {
		ret =  -ENOMEM;
		goto error;
	}

	pconfig->num = pdi->num;
	pconfig->ch_mask = (1 << ch) - 1;

	ret = sdw_stream_add_master(&cdns->bus, &sconfig,
				    pconfig, 1, dai_runtime->stream);
	if (ret)
		dev_err(cdns->dev, "add master to stream failed:%d\n", ret);

	kfree(pconfig);
error:
	return ret;
}

static int intel_prepare(struct snd_pcm_substream *substream,
			 struct snd_soc_dai *dai)
{
	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	struct sdw_cdns_dai_runtime *dai_runtime;
	int ch, dir;
	int ret = 0;

	dai_runtime = cdns->dai_runtime_array[dai->id];
	if (!dai_runtime) {
		dev_err(dai->dev, "failed to get dai runtime in %s\n",
			__func__);
		return -EIO;
	}

	if (dai_runtime->suspended) {
		struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
		struct snd_pcm_hw_params *hw_params;

		hw_params = &rtd->dpcm[substream->stream].hw_params;

		dai_runtime->suspended = false;

		/*
		 * .prepare() is called after system resume, where we
		 * need to reinitialize the SHIM/ALH/Cadence IP.
		 * .prepare() is also called to deal with underflows,
		 * but in those cases we cannot touch ALH/SHIM
		 * registers
		 */

		/* configure stream */
		ch = params_channels(hw_params);
		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
			dir = SDW_DATA_DIR_RX;
		else
			dir = SDW_DATA_DIR_TX;

		/* the SHIM will be configured in the callback functions */

		sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);

		/* Inform DSP about PDI stream number */
		ret = intel_params_stream(sdw, substream, dai,
					  hw_params,
					  sdw->instance,
					  dai_runtime->pdi->intel_alh_id);
	}

	return ret;
}

static int
intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	struct sdw_cdns_dai_runtime *dai_runtime;
	int ret;

	dai_runtime = cdns->dai_runtime_array[dai->id];
	if (!dai_runtime)
		return -EIO;

	/*
	 * The sdw stream state will transition to RELEASED when stream->
	 * master_list is empty. So the stream state will transition to
	 * DEPREPARED for the first cpu-dai and to RELEASED for the last
	 * cpu-dai.
	 */
	ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream);
	if (ret < 0) {
		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
			dai_runtime->stream->name, ret);
		return ret;
	}

	ret = intel_free_stream(sdw, substream, dai, sdw->instance);
	if (ret < 0) {
		dev_err(dai->dev, "intel_free_stream: failed %d\n", ret);
		return ret;
	}

	dai_runtime->pdi = NULL;

	return 0;
}

static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai,
				    void *stream, int direction)
{
	return cdns_set_sdw_stream(dai, stream, direction);
}

static void *intel_get_sdw_stream(struct snd_soc_dai *dai,
				  int direction)
{
	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
	struct sdw_cdns_dai_runtime *dai_runtime;

	dai_runtime = cdns->dai_runtime_array[dai->id];
	if (!dai_runtime)
		return ERR_PTR(-EINVAL);

	return dai_runtime->stream;
}

static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	struct sdw_intel_link_res *res = sdw->link_res;
	struct sdw_cdns_dai_runtime *dai_runtime;
	int ret = 0;

	/*
	 * The .trigger callback is used to program HDaudio DMA and send required IPC to audio
	 * firmware.
	 */
	if (res->ops && res->ops->trigger) {
		ret = res->ops->trigger(substream, cmd, dai);
		if (ret < 0)
			return ret;
	}

	dai_runtime = cdns->dai_runtime_array[dai->id];
	if (!dai_runtime) {
		dev_err(dai->dev, "failed to get dai runtime in %s\n",
			__func__);
		return -EIO;
	}

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_SUSPEND:

		/*
		 * The .prepare callback is used to deal with xruns and resume operations.
		 * In the case of xruns, the DMAs and SHIM registers cannot be touched,
		 * but for resume operations the DMAs and SHIM registers need to be initialized.
		 * the .trigger callback is used to track the suspend case only.
		 */

		dai_runtime->suspended = true;

		break;

	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		dai_runtime->paused = true;
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		dai_runtime->paused = false;
		break;
	default:
		break;
	}

	return ret;
}

static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
	.hw_params = intel_hw_params,
	.prepare = intel_prepare,
	.hw_free = intel_hw_free,
	.trigger = intel_trigger,
	.set_stream = intel_pcm_set_sdw_stream,
	.get_stream = intel_get_sdw_stream,
};

static const struct snd_soc_component_driver dai_component = {
+96 −16
Original line number Diff line number Diff line
@@ -23,9 +23,6 @@
#include "intel.h"
#include "intel_auxdevice.h"

/* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */
#define INTEL_DEV_NUM_IDA_MIN           4

#define INTEL_MASTER_SUSPEND_DELAY_MS	3000

/*
@@ -44,6 +41,39 @@ static int md_flags;
module_param_named(sdw_md_flags, md_flags, int, 0444);
MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");

struct wake_capable_part {
	const u16 mfg_id;
	const u16 part_id;
};

static struct wake_capable_part wake_capable_list[] = {
	{0x025d, 0x5682},
	{0x025d, 0x700},
	{0x025d, 0x711},
	{0x025d, 0x1712},
	{0x025d, 0x1713},
	{0x025d, 0x1716},
	{0x025d, 0x1717},
	{0x025d, 0x712},
	{0x025d, 0x713},
	{0x025d, 0x714},
	{0x025d, 0x715},
	{0x025d, 0x716},
	{0x025d, 0x717},
	{0x025d, 0x722},
};

static bool is_wake_capable(struct sdw_slave *slave)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(wake_capable_list); i++)
		if (slave->id.part_id == wake_capable_list[i].part_id &&
		    slave->id.mfg_id == wake_capable_list[i].mfg_id)
			return true;
	return false;
}

static int generic_pre_bank_switch(struct sdw_bus *bus)
{
	struct sdw_cdns *cdns = bus_to_cdns(bus);
@@ -60,18 +90,32 @@ static int generic_post_bank_switch(struct sdw_bus *bus)
	return sdw->link_res->hw_ops->post_bank_switch(sdw);
}

static void generic_new_peripheral_assigned(struct sdw_bus *bus, int dev_num)
static void generic_new_peripheral_assigned(struct sdw_bus *bus,
					    struct sdw_slave *slave,
					    int dev_num)
{
	struct sdw_cdns *cdns = bus_to_cdns(bus);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	int dev_num_min;
	int dev_num_max;
	bool wake_capable = slave->prop.wake_capable || is_wake_capable(slave);

	if (wake_capable) {
		dev_num_min = SDW_INTEL_DEV_NUM_IDA_MIN;
		dev_num_max = SDW_MAX_DEVICES;
	} else {
		dev_num_min = 1;
		dev_num_max = SDW_INTEL_DEV_NUM_IDA_MIN - 1;
	}

	/* paranoia check, this should never happen */
	if (dev_num < INTEL_DEV_NUM_IDA_MIN || dev_num > SDW_MAX_DEVICES)  {
		dev_err(bus->dev, "%s: invalid dev_num %d\n", __func__, dev_num);
	if (dev_num < dev_num_min || dev_num > dev_num_max)  {
		dev_err(bus->dev, "%s: invalid dev_num %d, wake supported %d\n",
			__func__, dev_num, slave->prop.wake_capable);
		return;
	}

	if (sdw->link_res->hw_ops->program_sdi)
	if (sdw->link_res->hw_ops->program_sdi && wake_capable)
		sdw->link_res->hw_ops->program_sdi(sdw, dev_num);
}

@@ -123,6 +167,30 @@ static int intel_prop_read(struct sdw_bus *bus)
	return 0;
}

static DEFINE_IDA(intel_peripheral_ida);

static int intel_get_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave)
{
	int bit;

	if (slave->prop.wake_capable || is_wake_capable(slave))
		return ida_alloc_range(&intel_peripheral_ida,
				       SDW_INTEL_DEV_NUM_IDA_MIN, SDW_MAX_DEVICES,
				       GFP_KERNEL);

	bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES);
	if (bit == SDW_MAX_DEVICES)
		return -ENODEV;

	return bit;
}

static void intel_put_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave)
{
	if (slave->prop.wake_capable || is_wake_capable(slave))
		ida_free(&intel_peripheral_ida, slave->dev_num);
}

static struct sdw_master_ops sdw_intel_ops = {
	.read_prop = intel_prop_read,
	.override_adr = sdw_dmi_override_adr,
@@ -132,6 +200,8 @@ static struct sdw_master_ops sdw_intel_ops = {
	.pre_bank_switch = generic_pre_bank_switch,
	.post_bank_switch = generic_post_bank_switch,
	.read_ping_status = cdns_read_ping_status,
	.get_device_num =  intel_get_device_num_ida,
	.put_device_num = intel_put_device_num_ida,
	.new_peripheral_assigned = generic_new_peripheral_assigned,
};

@@ -165,7 +235,6 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
	cdns->msg_count = 0;

	bus->link_id = auxdev->id;
	bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
	bus->clk_stop_timeout = 1;

	sdw_cdns_probe(cdns);
@@ -248,13 +317,6 @@ int intel_link_startup(struct auxiliary_device *auxdev)

	sdw_intel_debugfs_init(sdw);

	/* start bus */
	ret = sdw_intel_start_bus(sdw);
	if (ret) {
		dev_err(dev, "bus start failed: %d\n", ret);
		goto err_power_up;
	}

	/* Enable runtime PM */
	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
		pm_runtime_set_autosuspend_delay(dev,
@@ -264,6 +326,15 @@ int intel_link_startup(struct auxiliary_device *auxdev)

		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);

		pm_runtime_resume(bus->dev);
	}

	/* start bus */
	ret = sdw_intel_start_bus(sdw);
	if (ret) {
		dev_err(dev, "bus start failed: %d\n", ret);
		goto err_pm_runtime;
	}

	clock_stop_quirks = sdw->link_res->clock_stop_quirks;
@@ -293,12 +364,18 @@ int intel_link_startup(struct auxiliary_device *auxdev)
	 * with a delay. A more complete solution would require the
	 * definition of Master properties.
	 */
	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) {
		pm_runtime_mark_last_busy(bus->dev);
		pm_runtime_mark_last_busy(dev);
		pm_runtime_idle(dev);
	}

	sdw->startup_done = true;
	return 0;

err_pm_runtime:
	if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME))
		pm_runtime_disable(dev);
err_power_up:
	sdw_intel_link_power_down(sdw);
err_init:
@@ -552,6 +629,8 @@ static int __maybe_unused intel_resume(struct device *dev)
		pm_runtime_mark_last_busy(dev);
		pm_runtime_enable(dev);

		pm_runtime_resume(bus->dev);

		link_flags = md_flags >> (bus->link_id * 8);

		if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
@@ -587,6 +666,7 @@ static int __maybe_unused intel_resume(struct device *dev)
	 * counters and delay the pm_runtime suspend by several
	 * seconds, by when all enumeration should be complete.
	 */
	pm_runtime_mark_last_busy(bus->dev);
	pm_runtime_mark_last_busy(dev);

	return 0;
+0 −1
Original line number Diff line number Diff line
@@ -10,7 +10,6 @@
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+7 −5
Original line number Diff line number Diff line
@@ -858,6 +858,8 @@ struct sdw_defer {
 * @post_bank_switch: Callback for post bank switch
 * @read_ping_status: Read status from PING frames, reported with two bits per Device.
 * Bits 31:24 are reserved.
 * @get_device_num: Callback for vendor-specific device_number allocation
 * @put_device_num: Callback for vendor-specific device_number release
 * @new_peripheral_assigned: Callback to handle enumeration of new peripheral.
 */
struct sdw_master_ops {
@@ -873,7 +875,11 @@ struct sdw_master_ops {
	int (*pre_bank_switch)(struct sdw_bus *bus);
	int (*post_bank_switch)(struct sdw_bus *bus);
	u32 (*read_ping_status)(struct sdw_bus *bus);
	void (*new_peripheral_assigned)(struct sdw_bus *bus, int dev_num);
	int (*get_device_num)(struct sdw_bus *bus, struct sdw_slave *slave);
	void (*put_device_num)(struct sdw_bus *bus, struct sdw_slave *slave);
	void (*new_peripheral_assigned)(struct sdw_bus *bus,
					struct sdw_slave *slave,
					int dev_num);
};

/**
@@ -908,9 +914,6 @@ struct sdw_master_ops {
 * meaningful if multi_link is set. If set to 1, hardware-based
 * synchronization will be used even if a stream only uses a single
 * SoundWire segment.
 * @dev_num_ida_min: if set, defines the minimum values for the IDA
 * used to allocate system-unique device numbers. This value needs to be
 * identical across all SoundWire bus in the system.
 */
struct sdw_bus {
	struct device *dev;
@@ -939,7 +942,6 @@ struct sdw_bus {
	u32 bank_switch_timeout;
	bool multi_link;
	int hw_sync_min_links;
	int dev_num_ida_min;
};

int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
Loading