From bcac59029955ae57d3c660a0a1d6d4c7ae78fb49 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 May 2020 04:35:51 +0800 Subject: soundwire: add Slave sysfs support Expose MIPI DisCo Slave properties in sysfs. For Slave properties and Data Port 0, the attributes are managed with simple devm_ support. A Slave Device may have more than one Data Port (DPN), and each Data Port can be sink or source. The attributes are created dynamically using pre-canned macros, but still use devm_ with a name attribute group to avoid creating kobjects - as requested by GregKH. In the _show function, we use container_of() to retrieve port number and direction required to extract the information. Audio modes are not supported for now. Depending on the discussions the SoundWire Device Class, we may add it later as is or follow the new specification. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20200518203551.2053-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/sysfs_slave.c | 215 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 drivers/soundwire/sysfs_slave.c (limited to 'drivers/soundwire/sysfs_slave.c') diff --git a/drivers/soundwire/sysfs_slave.c b/drivers/soundwire/sysfs_slave.c new file mode 100644 index 000000000000..ae6d241eca50 --- /dev/null +++ b/drivers/soundwire/sysfs_slave.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2015-2020 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include "bus.h" +#include "sysfs_local.h" + +/* + * Slave sysfs + */ + +/* + * The sysfs for Slave reflects the MIPI description as given + * in the MIPI DisCo spec + * + * Base file is device + * |---- modalias + * |---- dev-properties + * |---- mipi_revision + * |---- wake_capable + * |---- test_mode_capable + * |---- clk_stop_mode1 + * |---- simple_clk_stop_capable + * |---- clk_stop_timeout + * |---- ch_prep_timeout + * |---- reset_behave + * |---- high_PHY_capable + * |---- paging_support + * |---- bank_delay_support + * |---- p15_behave + * |---- master_count + * |---- source_ports + * |---- sink_ports + * |---- dp0 + * |---- max_word + * |---- min_word + * |---- words + * |---- BRA_flow_controlled + * |---- simple_ch_prep_sm + * |---- imp_def_interrupts + * |---- dpN_ + * |---- max_word + * |---- min_word + * |---- words + * |---- type + * |---- max_grouping + * |---- simple_ch_prep_sm + * |---- ch_prep_timeout + * |---- imp_def_interrupts + * |---- min_ch + * |---- max_ch + * |---- channels + * |---- ch_combinations + * |---- max_async_buffer + * |---- block_pack_mode + * |---- port_encoding + * + */ + +#define sdw_slave_attr(field, format_string) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct sdw_slave *slave = dev_to_sdw_dev(dev); \ + return sprintf(buf, format_string, slave->prop.field); \ +} \ +static DEVICE_ATTR_RO(field) + +sdw_slave_attr(mipi_revision, "0x%x\n"); +sdw_slave_attr(wake_capable, "%d\n"); +sdw_slave_attr(test_mode_capable, "%d\n"); +sdw_slave_attr(clk_stop_mode1, "%d\n"); +sdw_slave_attr(simple_clk_stop_capable, "%d\n"); +sdw_slave_attr(clk_stop_timeout, "%d\n"); +sdw_slave_attr(ch_prep_timeout, "%d\n"); +sdw_slave_attr(reset_behave, "%d\n"); +sdw_slave_attr(high_PHY_capable, "%d\n"); +sdw_slave_attr(paging_support, "%d\n"); +sdw_slave_attr(bank_delay_support, "%d\n"); +sdw_slave_attr(p15_behave, "%d\n"); +sdw_slave_attr(master_count, "%d\n"); +sdw_slave_attr(source_ports, "0x%x\n"); +sdw_slave_attr(sink_ports, "0x%x\n"); + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + + return sdw_slave_modalias(slave, buf, 256); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *slave_attrs[] = { + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(slave); + +static struct attribute *slave_dev_attrs[] = { + &dev_attr_mipi_revision.attr, + &dev_attr_wake_capable.attr, + &dev_attr_test_mode_capable.attr, + &dev_attr_clk_stop_mode1.attr, + &dev_attr_simple_clk_stop_capable.attr, + &dev_attr_clk_stop_timeout.attr, + &dev_attr_ch_prep_timeout.attr, + &dev_attr_reset_behave.attr, + &dev_attr_high_PHY_capable.attr, + &dev_attr_paging_support.attr, + &dev_attr_bank_delay_support.attr, + &dev_attr_p15_behave.attr, + &dev_attr_master_count.attr, + &dev_attr_source_ports.attr, + &dev_attr_sink_ports.attr, + NULL, +}; + +/* + * we don't use ATTRIBUTES_GROUP here since we want to add a subdirectory + * for device-level properties + */ +static struct attribute_group sdw_slave_dev_attr_group = { + .attrs = slave_dev_attrs, + .name = "dev-properties", +}; + +/* + * DP0 sysfs + */ + +#define sdw_dp0_attr(field, format_string) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct sdw_slave *slave = dev_to_sdw_dev(dev); \ + return sprintf(buf, format_string, slave->prop.dp0_prop->field);\ +} \ +static DEVICE_ATTR_RO(field) + +sdw_dp0_attr(max_word, "%d\n"); +sdw_dp0_attr(min_word, "%d\n"); +sdw_dp0_attr(BRA_flow_controlled, "%d\n"); +sdw_dp0_attr(simple_ch_prep_sm, "%d\n"); +sdw_dp0_attr(imp_def_interrupts, "0x%x\n"); + +static ssize_t words_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + ssize_t size = 0; + int i; + + for (i = 0; i < slave->prop.dp0_prop->num_words; i++) + size += sprintf(buf + size, "%d ", + slave->prop.dp0_prop->words[i]); + size += sprintf(buf + size, "\n"); + + return size; +} +static DEVICE_ATTR_RO(words); + +static struct attribute *dp0_attrs[] = { + &dev_attr_max_word.attr, + &dev_attr_min_word.attr, + &dev_attr_words.attr, + &dev_attr_BRA_flow_controlled.attr, + &dev_attr_simple_ch_prep_sm.attr, + &dev_attr_imp_def_interrupts.attr, + NULL, +}; + +/* + * we don't use ATTRIBUTES_GROUP here since we want to add a subdirectory + * for dp0-level properties + */ +static const struct attribute_group dp0_group = { + .attrs = dp0_attrs, + .name = "dp0", +}; + +int sdw_slave_sysfs_init(struct sdw_slave *slave) +{ + int ret; + + ret = devm_device_add_groups(&slave->dev, slave_groups); + if (ret < 0) + return ret; + + ret = devm_device_add_group(&slave->dev, &sdw_slave_dev_attr_group); + if (ret < 0) + return ret; + + if (slave->prop.dp0_prop) { + ret = devm_device_add_group(&slave->dev, &dp0_group); + if (ret < 0) + return ret; + } + + if (slave->prop.source_ports || slave->prop.sink_ports) { + ret = sdw_slave_sysfs_dpn_init(slave); + if (ret < 0) + return ret; + } + + return 0; +} + -- cgit v1.2.3