linux-stable/drivers/soundwire/master.c
Pierre-Louis Bossart 6543ac13c6 soundwire: bus: introduce controller_id
The existing SoundWire support misses a clear Controller/Manager
hiearchical definition to deal with all variants across SOC vendors.

a) Intel platforms have one controller with 4 or more Managers.
b) AMD platforms have two controllers with one Manager each, but due
to BIOS issues use two different link_id values within the scope of a
single controller.
c) QCOM platforms have one or more controller with one Manager each.

This patch adds a 'controller_id' which can be set by higher
levels. If assigned to -1, the controller_id will be set to the
system-unique IDA-assigned bus->id.

The main change is that the bus->id is no longer used for any device
name, which makes the definition completely predictable and not
dependent on any enumeration order. The bus->id is only used to insert
the Managers in the stream rt context.

Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Tested-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/stable/20231017160933.12624-2-pierre-louis.bossart%40linux.intel.com
Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20231017160933.12624-2-pierre-louis.bossart@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
2023-11-24 12:24:37 +05:30

188 lines
4.9 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2019-2020 Intel Corporation.
#include <linux/device.h>
#include <linux/acpi.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "bus.h"
/*
* The 3s value for autosuspend will only be used if there are no
* devices physically attached on a bus segment. In practice enabling
* the bus operation will result in children devices become active and
* the master device will only suspend when all its children are no
* longer active.
*/
#define SDW_MASTER_SUSPEND_DELAY_MS 3000
/*
* The sysfs for properties reflects the MIPI description as given
* in the MIPI DisCo spec
*
* Base file is:
* sdw-master-N
* |---- revision
* |---- clk_stop_modes
* |---- max_clk_freq
* |---- clk_freq
* |---- clk_gears
* |---- default_row
* |---- default_col
* |---- dynamic_shape
* |---- err_threshold
*/
#define sdw_master_attr(field, format_string) \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct sdw_master_device *md = dev_to_sdw_master_device(dev); \
return sprintf(buf, format_string, md->bus->prop.field); \
} \
static DEVICE_ATTR_RO(field)
sdw_master_attr(revision, "0x%x\n");
sdw_master_attr(clk_stop_modes, "0x%x\n");
sdw_master_attr(max_clk_freq, "%d\n");
sdw_master_attr(default_row, "%d\n");
sdw_master_attr(default_col, "%d\n");
sdw_master_attr(default_frame_rate, "%d\n");
sdw_master_attr(dynamic_frame, "%d\n");
sdw_master_attr(err_threshold, "%d\n");
static ssize_t clock_frequencies_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sdw_master_device *md = dev_to_sdw_master_device(dev);
ssize_t size = 0;
int i;
for (i = 0; i < md->bus->prop.num_clk_freq; i++)
size += sprintf(buf + size, "%8d ",
md->bus->prop.clk_freq[i]);
size += sprintf(buf + size, "\n");
return size;
}
static DEVICE_ATTR_RO(clock_frequencies);
static ssize_t clock_gears_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sdw_master_device *md = dev_to_sdw_master_device(dev);
ssize_t size = 0;
int i;
for (i = 0; i < md->bus->prop.num_clk_gears; i++)
size += sprintf(buf + size, "%8d ",
md->bus->prop.clk_gears[i]);
size += sprintf(buf + size, "\n");
return size;
}
static DEVICE_ATTR_RO(clock_gears);
static struct attribute *master_node_attrs[] = {
&dev_attr_revision.attr,
&dev_attr_clk_stop_modes.attr,
&dev_attr_max_clk_freq.attr,
&dev_attr_default_row.attr,
&dev_attr_default_col.attr,
&dev_attr_default_frame_rate.attr,
&dev_attr_dynamic_frame.attr,
&dev_attr_err_threshold.attr,
&dev_attr_clock_frequencies.attr,
&dev_attr_clock_gears.attr,
NULL,
};
ATTRIBUTE_GROUPS(master_node);
static void sdw_master_device_release(struct device *dev)
{
struct sdw_master_device *md = dev_to_sdw_master_device(dev);
kfree(md);
}
static const struct dev_pm_ops master_dev_pm = {
SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
pm_generic_runtime_resume, NULL)
};
struct device_type sdw_master_type = {
.name = "soundwire_master",
.release = sdw_master_device_release,
.pm = &master_dev_pm,
};
/**
* sdw_master_device_add() - create a Linux Master Device representation.
* @bus: SDW bus instance
* @parent: parent device
* @fwnode: firmware node handle
*/
int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
struct fwnode_handle *fwnode)
{
struct sdw_master_device *md;
int ret;
if (!parent)
return -EINVAL;
md = kzalloc(sizeof(*md), GFP_KERNEL);
if (!md)
return -ENOMEM;
md->dev.bus = &sdw_bus_type;
md->dev.type = &sdw_master_type;
md->dev.parent = parent;
md->dev.groups = master_node_groups;
md->dev.of_node = parent->of_node;
md->dev.fwnode = fwnode;
md->dev.dma_mask = parent->dma_mask;
dev_set_name(&md->dev, "sdw-master-%d-%d", bus->controller_id, bus->link_id);
ret = device_register(&md->dev);
if (ret) {
dev_err(parent, "Failed to add master: ret %d\n", ret);
/*
* On err, don't free but drop ref as this will be freed
* when release method is invoked.
*/
put_device(&md->dev);
goto device_register_err;
}
/* add shortcuts to improve code readability/compactness */
md->bus = bus;
bus->dev = &md->dev;
bus->md = md;
pm_runtime_set_autosuspend_delay(&bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&bus->md->dev);
pm_runtime_mark_last_busy(&bus->md->dev);
pm_runtime_set_active(&bus->md->dev);
pm_runtime_enable(&bus->md->dev);
pm_runtime_idle(&bus->md->dev);
device_register_err:
return ret;
}
/**
* sdw_master_device_del() - delete a Linux Master Device representation.
* @bus: bus handle
*
* This function is the dual of sdw_master_device_add()
*/
int sdw_master_device_del(struct sdw_bus *bus)
{
pm_runtime_disable(&bus->md->dev);
device_unregister(bus->dev);
return 0;
}