From 611905d6e31c6ce2a59c8bda91b3d33beec13c50 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 20 Apr 2020 16:01:01 +0900 Subject: soundwire: intel: use asoc_rtd_to_cpu() / asoc_rtd_to_codec() macro for DAI pointer Now ALSA SoC needs to use asoc_rtd_to_codec(), otherwise, it will be compile error. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87y2qqvdxu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Vinod Koul --- 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 3c83e76c6bf9..ed8d576bf5dc 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -669,11 +669,11 @@ static int sdw_stream_setup(struct snd_pcm_substream *substream, /* Set stream pointer on all CODEC DAIs */ for (i = 0; i < rtd->num_codecs; i++) { - ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sdw_stream, + ret = snd_soc_dai_set_sdw_stream(asoc_rtd_to_codec(rtd, i), sdw_stream, substream->stream); if (ret < 0) { dev_err(dai->dev, "failed to set stream pointer on codec dai %s", - rtd->codec_dais[i]->name); + asoc_rtd_to_codec(rtd, i)->name); goto release_stream; } } -- cgit v1.2.3 From 91b5cfc0209b63b6750bed496c4fe24eb2f48f58 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 30 Apr 2020 02:50:57 +0800 Subject: soundwire: qcom: fix error handling in probe Make sure all error cases are properly handled and all resources freed. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200429185057.12810-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index d6c9ad231873..e08a17c13f92 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -765,12 +765,16 @@ static int qcom_swrm_probe(struct platform_device *pdev) } ctrl->irq = of_irq_get(dev->of_node, 0); - if (ctrl->irq < 0) - return ctrl->irq; + if (ctrl->irq < 0) { + ret = ctrl->irq; + goto err_init; + } ctrl->hclk = devm_clk_get(dev, "iface"); - if (IS_ERR(ctrl->hclk)) - return PTR_ERR(ctrl->hclk); + if (IS_ERR(ctrl->hclk)) { + ret = PTR_ERR(ctrl->hclk); + goto err_init; + } clk_prepare_enable(ctrl->hclk); @@ -787,7 +791,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) ret = qcom_swrm_get_port_config(ctrl); if (ret) - return ret; + goto err_clk; params = &ctrl->bus.params; params->max_dr_freq = DEFAULT_CLK_FREQ; @@ -814,28 +818,32 @@ static int qcom_swrm_probe(struct platform_device *pdev) "soundwire", ctrl); if (ret) { dev_err(dev, "Failed to request soundwire irq\n"); - goto err; + goto err_clk; } ret = sdw_add_bus_master(&ctrl->bus); if (ret) { dev_err(dev, "Failed to register Soundwire controller (%d)\n", ret); - goto err; + goto err_clk; } qcom_swrm_init(ctrl); ret = qcom_swrm_register_dais(ctrl); if (ret) - goto err; + goto err_master_add; dev_info(dev, "Qualcomm Soundwire controller v%x.%x.%x Registered\n", (ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff, ctrl->version & 0xffff); return 0; -err: + +err_master_add: + sdw_delete_bus_master(&ctrl->bus); +err_clk: clk_disable_unprepare(ctrl->hclk); +err_init: return ret; } -- cgit v1.2.3 From 01de995075b20f58a77f408cdf49034682487429 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 20 Apr 2020 02:51:14 +0800 Subject: Documentation: SoundWire: clarify TDM mode support The current description of stream topologies does not explicitly mention 'mirror' topologies used for audio amplifiers, where all amplifiers see the same data and generate a different output based on configuration or dynamic information. Add examples and notes to explain how channels can be transmitted and mapped. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200419185117.4233-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- Documentation/driver-api/soundwire/stream.rst | 89 ++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst index 8bceece51554..1b386076402c 100644 --- a/Documentation/driver-api/soundwire/stream.rst +++ b/Documentation/driver-api/soundwire/stream.rst @@ -75,8 +75,33 @@ Slaves are using single port. :: | (Data) | +---------------+ +Example 4: Stereo Stream with L and R channels is rendered by +Master. Both of the L and R channels are received by two different +Slaves. Master and both Slaves are using single port handling +L+R. Each Slave device processes the L + R data locally, typically +based on static configuration or dynamic orientation, and may drive +one or more speakers. :: -Example 4: Stereo Stream with L and R channel is rendered by two different + +---------------+ Clock Signal +---------------+ + | Master +---------+------------------------+ Slave | + | Interface | | | Interface | + | | | | 1 | + | | | Data Signal | | + | L + R +---+------------------------------+ L + R | + | (Data) | | | Data Direction | (Data) | + +---------------+ | | +-------------> +---------------+ + | | + | | + | | +---------------+ + | +----------------------> | Slave | + | | Interface | + | | 2 | + | | | + +----------------------------> | L + R | + | (Data) | + +---------------+ + +Example 5: Stereo Stream with L and R channel is rendered by two different Ports of the Master and is received by only single Port of the Slave interface. :: @@ -101,7 +126,7 @@ interface. :: +--------------------+ | | +----------------+ -Example 5: Stereo Stream with L and R channel is rendered by 2 Masters, each +Example 6: Stereo Stream with L and R channel is rendered by 2 Masters, each rendering one channel, and is received by two different Slaves, each receiving one channel. Both Masters and both Slaves are using single port. :: @@ -123,12 +148,70 @@ receiving one channel. Both Masters and both Slaves are using single port. :: | (Data) | Data Direction | (Data) | +---------------+ +-----------------------> +---------------+ -Note: In multi-link cases like above, to lock, one would acquire a global +Example 7: Stereo Stream with L and R channel is rendered by 2 +Masters, each rendering both channels. Each Slave receives L + R. This +is the same application as Example 4 but with Slaves placed on +separate links. :: + + +---------------+ Clock Signal +---------------+ + | Master +----------------------------------+ Slave | + | Interface | | Interface | + | 1 | | 1 | + | | Data Signal | | + | L + R +----------------------------------+ L + R | + | (Data) | Data Direction | (Data) | + +---------------+ +-----------------------> +---------------+ + + +---------------+ Clock Signal +---------------+ + | Master +----------------------------------+ Slave | + | Interface | | Interface | + | 2 | | 2 | + | | Data Signal | | + | L + R +----------------------------------+ L + R | + | (Data) | Data Direction | (Data) | + +---------------+ +-----------------------> +---------------+ + +Example 8: 4-channel Stream is rendered by 2 Masters, each rendering a +2 channels. Each Slave receives 2 channels. :: + + +---------------+ Clock Signal +---------------+ + | Master +----------------------------------+ Slave | + | Interface | | Interface | + | 1 | | 1 | + | | Data Signal | | + | L1 + R1 +----------------------------------+ L1 + R1 | + | (Data) | Data Direction | (Data) | + +---------------+ +-----------------------> +---------------+ + + +---------------+ Clock Signal +---------------+ + | Master +----------------------------------+ Slave | + | Interface | | Interface | + | 2 | | 2 | + | | Data Signal | | + | L2 + R2 +----------------------------------+ L2 + R2 | + | (Data) | Data Direction | (Data) | + +---------------+ +-----------------------> +---------------+ + +Note1: In multi-link cases like above, to lock, one would acquire a global lock and then go on locking bus instances. But, in this case the caller framework(ASoC DPCM) guarantees that stream operations on a card are always serialized. So, there is no race condition and hence no need for global lock. +Note2: A Slave device may be configured to receive all channels +transmitted on a link for a given Stream (Example 4) or just a subset +of the data (Example 3). The configuration of the Slave device is not +handled by a SoundWire subsystem API, but instead by the +snd_soc_dai_set_tdm_slot() API. The platform or machine driver will +typically configure which of the slots are used. For Example 4, the +same slots would be used by all Devices, while for Example 3 the Slave +Device1 would use e.g. Slot 0 and Slave device2 slot 1. + +Note3: Multiple Sink ports can extract the same information for the +same bitSlots in the SoundWire frame, however multiple Source ports +shall be configured with different bitSlot configurations. This is the +same limitation as with I2S/PCM TDM usages. + SoundWire Stream Management flow ================================ -- cgit v1.2.3 From 8893ab5e8ee5d7c12e0fc1dca4a309475064473d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 20 Apr 2020 02:51:15 +0800 Subject: soundwire: slave: don't init debugfs on device registration error The error handling flow seems incorrect, there is no reason to try and add debugfs support if the device registration did not succeed. Return on error. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20200419185117.4233-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/slave.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index aace57fae7f8..4bacdb187eab 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -68,6 +68,8 @@ static int sdw_slave_add(struct sdw_bus *bus, list_del(&slave->node); mutex_unlock(&bus->bus_lock); put_device(&slave->dev); + + return ret; } sdw_slave_debugfs_init(slave); -- cgit v1.2.3 From 88ac86f252511d6d6e8a71c79957222db0389114 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 20 Apr 2020 02:51:16 +0800 Subject: soundwire: debugfs: clarify SDPX license with GPL-2.0-only Follow recommendation to use GPL-2.0-only Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20200419185117.4233-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c index fb1140e82b86..b6cad0d59b7b 100644 --- a/drivers/soundwire/debugfs.c +++ b/drivers/soundwire/debugfs.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only // Copyright(c) 2017-2019 Intel Corporation. #include -- cgit v1.2.3 From f48f4fd9fefb1e3a2f6bc249aef88f951be58e17 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 20 Apr 2020 02:51:17 +0800 Subject: soundwire: bus: reduce verbosity on enumeration No need to repeat the same info log on all enumerations (essentially each power-up), keep it as debug information. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200419185117.4233-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 488c3c9e4947..f9a078ce76bc 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -563,9 +563,9 @@ static int sdw_assign_device_num(struct sdw_slave *slave) } if (!new_device) - dev_info(slave->bus->dev, - "Slave already registered, reusing dev_num:%d\n", - slave->dev_num); + dev_dbg(slave->bus->dev, + "Slave already registered, reusing dev_num:%d\n", + slave->dev_num); /* Clear the slave->dev_num to transfer message on device 0 */ dev_num = slave->dev_num; -- cgit v1.2.3 From 4f1738f4c24b4485b899ccd9dbdd4f2fcbe6af2d Mon Sep 17 00:00:00 2001 From: Samuel Zou Date: Wed, 6 May 2020 11:25:53 +0800 Subject: soundwire: qcom: Use IRQF_ONESHOT Fixes coccicheck error: drivers/soundwire/qcom.c:815:7-32: ERROR: Threaded IRQ with no primary handler requested without IRQF_ONESHOT Reported-by: Hulk Robot Signed-off-by: Samuel Zou Link: https://lore.kernel.org/r/1588735553-34219-1-git-send-email-zou_wei@huawei.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index e08a17c13f92..a2a35f780fc3 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -814,7 +814,8 @@ static int qcom_swrm_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, ctrl->irq, NULL, qcom_swrm_irq_handler, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "soundwire", ctrl); if (ret) { dev_err(dev, "Failed to request soundwire irq\n"); -- cgit v1.2.3 From f779ad09109eebd3176464fdf6729e3504b49bc5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 May 2020 02:30:45 +0200 Subject: soundwire: (cosmetic) remove multiple superfluous "else" statements No need for an "else" after a "return" statement. Remove multiple such occurrences in bus.c. Signed-off-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20200508003046.23162-2-guennadi.liakhovetski@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index f9a078ce76bc..4d8183cc323d 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -284,9 +284,10 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, msg->flags = flags; msg->buf = buf; - if (addr < SDW_REG_NO_PAGE) { /* no paging area */ + if (addr < SDW_REG_NO_PAGE) /* no paging area */ return 0; - } else if (addr >= SDW_REG_MAX) { /* illegal addr */ + + if (addr >= SDW_REG_MAX) { /* illegal addr */ pr_err("SDW: Invalid address %x passed\n", addr); return -EINVAL; } @@ -306,7 +307,9 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, if (!slave) { pr_err("SDW: No slave for paging addr\n"); return -EINVAL; - } else if (!slave->prop.paging_support) { + } + + if (!slave->prop.paging_support) { dev_err(&slave->dev, "address %x needs paging but no support\n", addr); return -EINVAL; @@ -375,8 +378,8 @@ sdw_bread_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr) ret = sdw_transfer(bus, &msg); if (ret < 0) return ret; - else - return buf; + + return buf; } static int @@ -471,8 +474,8 @@ int sdw_read(struct sdw_slave *slave, u32 addr) ret = sdw_nread(slave, addr, 1, &buf); if (ret < 0) return ret; - else - return buf; + + return buf; } EXPORT_SYMBOL(sdw_read); -- cgit v1.2.3 From 6f7219fecf35dd9d429b0ea93d78b4ee087960b7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 8 May 2020 02:30:46 +0200 Subject: soundwire: intel: (cosmetic) remove multiple superfluous "else" statements No need for an "else" after a "return" statement. Remove multiple such occurrences in Intel-specific code. Signed-off-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20200508003046.23162-3-guennadi.liakhovetski@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 8 ++++++-- drivers/soundwire/intel_init.c | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index ecd357d1c63d..9ea87538b9ef 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -407,7 +407,9 @@ cdns_fill_msg_resp(struct sdw_cdns *cdns, if (nack) { dev_err_ratelimited(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; - } else if (no_ack) { + } + + if (no_ack) { dev_dbg_ratelimited(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; } @@ -520,7 +522,9 @@ cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg) dev_err_ratelimited(cdns->dev, "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); return SDW_CMD_FAIL; - } else if (no_ack) { + } + + if (no_ack) { dev_dbg_ratelimited(cdns->dev, "SCP_addrpage ignored for Slave %d\n", msg->dev_num); return SDW_CMD_IGNORED; diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 4b769409f6f8..d5d42795a48f 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -86,7 +86,9 @@ static struct sdw_intel_ctx dev_err(&adev->dev, "Link count %d exceeds max %d\n", count, SDW_MAX_LINKS); return NULL; - } else if (!count) { + } + + if (!count) { dev_warn(&adev->dev, "No SoundWire links detected\n"); return NULL; } -- cgit v1.2.3 From 5cab3ff2489ede5abffa6ad730708e087dc45a4d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 May 2020 01:43:18 +0800 Subject: soundwire: bus: rename sdw_bus_master_add/delete, add arguments In preparation for future extensions, rename functions to use sdw_bus_master prefix and add a parent and fwnode argument to sdw_bus_master_add to help with device registration in follow-up patches. No functionality change, just renames and additional arguments. The Intel code is currently unused, the two additional arguments are only needed for compilation. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Acked-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20200518174322.31561-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- Documentation/driver-api/soundwire/summary.rst | 7 ++++--- drivers/soundwire/bus.c | 15 +++++++++------ drivers/soundwire/intel.c | 8 ++++---- drivers/soundwire/qcom.c | 6 +++--- include/linux/soundwire/sdw.h | 5 +++-- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Documentation/driver-api/soundwire/summary.rst b/Documentation/driver-api/soundwire/summary.rst index 8193125a2bfb..01dcb954f6d7 100644 --- a/Documentation/driver-api/soundwire/summary.rst +++ b/Documentation/driver-api/soundwire/summary.rst @@ -101,10 +101,11 @@ Following is the Bus API to register the SoundWire Bus: .. code-block:: c - int sdw_add_bus_master(struct sdw_bus *bus) + int sdw_bus_master_add(struct sdw_bus *bus, + struct device *parent, + struct fwnode_handle) { - if (!bus->dev) - return -ENODEV; + sdw_master_device_add(bus, parent, fwnode); mutex_init(&bus->lock); INIT_LIST_HEAD(&bus->slaves); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 4d8183cc323d..a8072791d6b8 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -10,13 +10,16 @@ #include "bus.h" /** - * sdw_add_bus_master() - add a bus Master instance + * sdw_bus_master_add() - add a bus Master instance * @bus: bus instance + * @parent: parent device + * @fwnode: firmware node handle * * Initializes the bus instance, read properties and create child * devices. */ -int sdw_add_bus_master(struct sdw_bus *bus) +int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, + struct fwnode_handle *fwnode) { struct sdw_master_prop *prop = NULL; int ret; @@ -107,7 +110,7 @@ int sdw_add_bus_master(struct sdw_bus *bus) return 0; } -EXPORT_SYMBOL(sdw_add_bus_master); +EXPORT_SYMBOL(sdw_bus_master_add); static int sdw_delete_slave(struct device *dev, void *data) { @@ -131,18 +134,18 @@ static int sdw_delete_slave(struct device *dev, void *data) } /** - * sdw_delete_bus_master() - delete the bus master instance + * sdw_bus_master_delete() - delete the bus master instance * @bus: bus to be deleted * * Remove the instance, delete the child devices. */ -void sdw_delete_bus_master(struct sdw_bus *bus) +void sdw_bus_master_delete(struct sdw_bus *bus) { device_for_each_child(bus->dev, NULL, sdw_delete_slave); sdw_bus_debugfs_exit(bus); } -EXPORT_SYMBOL(sdw_delete_bus_master); +EXPORT_SYMBOL(sdw_bus_master_delete); /* * SDW IO Calls diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index ed8d576bf5dc..430f8addef83 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1110,9 +1110,9 @@ static int intel_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sdw); - ret = sdw_add_bus_master(&sdw->cdns.bus); + ret = sdw_bus_master_add(&sdw->cdns.bus, &pdev->dev, pdev->dev.fwnode); if (ret) { - dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); + dev_err(&pdev->dev, "sdw_bus_master_add fail: %d\n", ret); return ret; } @@ -1173,7 +1173,7 @@ err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->link_res->irq, sdw); err_init: - sdw_delete_bus_master(&sdw->cdns.bus); + sdw_bus_master_delete(&sdw->cdns.bus); return ret; } @@ -1189,7 +1189,7 @@ static int intel_remove(struct platform_device *pdev) free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } - sdw_delete_bus_master(&sdw->cdns.bus); + sdw_bus_master_delete(&sdw->cdns.bus); return 0; } diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index a2a35f780fc3..ea03bb988f81 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -822,7 +822,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) goto err_clk; } - ret = sdw_add_bus_master(&ctrl->bus); + ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode); if (ret) { dev_err(dev, "Failed to register Soundwire controller (%d)\n", ret); @@ -841,7 +841,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) return 0; err_master_add: - sdw_delete_bus_master(&ctrl->bus); + sdw_bus_master_delete(&ctrl->bus); err_clk: clk_disable_unprepare(ctrl->hclk); err_init: @@ -852,7 +852,7 @@ static int qcom_swrm_remove(struct platform_device *pdev) { struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(&pdev->dev); - sdw_delete_bus_master(&ctrl->bus); + sdw_bus_master_delete(&ctrl->bus); clk_disable_unprepare(ctrl->hclk); return 0; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 00f5826092e3..2003e8c55538 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -832,8 +832,9 @@ struct sdw_bus { bool multi_link; }; -int sdw_add_bus_master(struct sdw_bus *bus); -void sdw_delete_bus_master(struct sdw_bus *bus); +int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, + struct fwnode_handle *fwnode); +void sdw_bus_master_delete(struct sdw_bus *bus); /** * sdw_port_config: Master or Slave Port configuration -- cgit v1.2.3 From 90acca1d54ad566b4af5f1030b4a4a2420ce2ef0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 May 2020 01:43:19 +0800 Subject: soundwire: bus_type: introduce sdw_slave_type and sdw_master_type this is a preparatory patch before the introduction of the sdw_master_type. The SoundWire slave support is slightly modified with the use of a sdw_slave_type, and the uevent handling move to slave.c (since it's not necessary for the master). No functionality change other than moving code around. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Acked-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20200518174322.31561-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus_type.c | 19 +++++++++++++------ drivers/soundwire/slave.c | 8 +++++++- include/linux/soundwire/sdw_type.h | 9 ++++++++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 17f096dd6806..2c1a19caba51 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -33,13 +33,21 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) { - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(ddrv); + struct sdw_slave *slave; + struct sdw_driver *drv; + int ret = 0; + + if (is_sdw_slave(dev)) { + slave = dev_to_sdw_dev(dev); + drv = drv_to_sdw_driver(ddrv); - return !!sdw_get_device_id(slave, drv); + ret = !!sdw_get_device_id(slave, drv); + } + return ret; } -int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) +static int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, + size_t size) { /* modalias is sdw:mp */ @@ -47,7 +55,7 @@ int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) slave->id.mfg_id, slave->id.part_id); } -static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) +int sdw_slave_uevent(struct device *dev, struct kobj_uevent_env *env) { struct sdw_slave *slave = dev_to_sdw_dev(dev); char modalias[32]; @@ -63,7 +71,6 @@ static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type sdw_bus_type = { .name = "soundwire", .match = sdw_bus_match, - .uevent = sdw_uevent, }; EXPORT_SYMBOL_GPL(sdw_bus_type); diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 4bacdb187eab..0839445ee07b 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -14,6 +14,12 @@ static void sdw_slave_release(struct device *dev) kfree(slave); } +struct device_type sdw_slave_type = { + .name = "sdw_slave", + .release = sdw_slave_release, + .uevent = sdw_slave_uevent, +}; + static int sdw_slave_add(struct sdw_bus *bus, struct sdw_slave_id *id, struct fwnode_handle *fwnode) { @@ -41,9 +47,9 @@ static int sdw_slave_add(struct sdw_bus *bus, id->class_id, id->unique_id); } - slave->dev.release = sdw_slave_release; slave->dev.bus = &sdw_bus_type; slave->dev.of_node = of_node_get(to_of_node(fwnode)); + slave->dev.type = &sdw_slave_type; slave->bus = bus; slave->status = SDW_SLAVE_UNATTACHED; init_completion(&slave->enumeration_complete); diff --git a/include/linux/soundwire/sdw_type.h b/include/linux/soundwire/sdw_type.h index aaa7f4267c14..52eb66cd11bc 100644 --- a/include/linux/soundwire/sdw_type.h +++ b/include/linux/soundwire/sdw_type.h @@ -5,6 +5,13 @@ #define __SOUNDWIRE_TYPES_H extern struct bus_type sdw_bus_type; +extern struct device_type sdw_slave_type; +extern struct device_type sdw_master_type; + +static inline int is_sdw_slave(const struct device *dev) +{ + return dev->type == &sdw_slave_type; +} #define drv_to_sdw_driver(_drv) container_of(_drv, struct sdw_driver, driver) @@ -14,7 +21,7 @@ extern struct bus_type sdw_bus_type; int __sdw_register_driver(struct sdw_driver *drv, struct module *owner); void sdw_unregister_driver(struct sdw_driver *drv); -int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); +int sdw_slave_uevent(struct device *dev, struct kobj_uevent_env *env); /** * module_sdw_driver() - Helper macro for registering a Soundwire driver -- cgit v1.2.3 From dbb50c7a9949506f750d59d9ba4d58f0ce8ccd42 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 19 May 2020 01:43:20 +0800 Subject: soundwire: bus: add unique bus id Adding an unique id for each bus. Suggested-by: Vinod Koul Signed-off-by: Bard Liao Acked-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20200518174322.31561-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 20 ++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index a8072791d6b8..03d72fa3c190 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -9,6 +9,19 @@ #include #include "bus.h" +static DEFINE_IDA(sdw_ida); + +static int sdw_get_id(struct sdw_bus *bus) +{ + int rc = ida_alloc(&sdw_ida, GFP_KERNEL); + + if (rc < 0) + return rc; + + bus->id = rc; + return 0; +} + /** * sdw_bus_master_add() - add a bus Master instance * @bus: bus instance @@ -29,6 +42,12 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, return -ENODEV; } + ret = sdw_get_id(bus); + if (ret) { + dev_err(bus->dev, "Failed to get bus id\n"); + return ret; + } + if (!bus->ops) { dev_err(bus->dev, "SoundWire Bus ops are not set\n"); return -EINVAL; @@ -144,6 +163,7 @@ void sdw_bus_master_delete(struct sdw_bus *bus) device_for_each_child(bus->dev, NULL, sdw_delete_slave); sdw_bus_debugfs_exit(bus); + ida_free(&sdw_ida, bus->id); } EXPORT_SYMBOL(sdw_bus_master_delete); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 2003e8c55538..a32cb26f1815 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -789,6 +789,7 @@ struct sdw_master_ops { * struct sdw_bus - SoundWire bus * @dev: Master linux device * @link_id: Link id number, can be 0 to N, unique for each Master + * @id: bus system-wide unique id * @slaves: list of Slaves on this bus * @assigned: Bitmap for Slave device numbers. * Bit set implies used number, bit clear implies unused number. @@ -813,6 +814,7 @@ struct sdw_master_ops { struct sdw_bus { struct device *dev; unsigned int link_id; + int id; struct list_head slaves; DECLARE_BITMAP(assigned, SDW_MAX_DEVICES); struct mutex bus_lock; -- cgit v1.2.3 From 7ceaa40b930e462ba0477ca6af34ec04d08181dc Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 May 2020 01:43:21 +0800 Subject: soundwire: bus_type: add sdw_master_device support In the existing SoundWire code, Master Devices are not explicitly represented - only SoundWire Slave Devices are exposed (the use of capital letters follows the SoundWire specification conventions). With the existing code, the bus is handled without using a proper device, and bus->dev typically points to a platform device. The right thing to do as discussed in multiple reviews is use a device for each bus. The sdw_master_device addition is done with minimal internal plumbing and not exposed externally. The existing API based on sdw_bus_master_add() and sdw_bus_master_delete() will deal with the sdw_master_device life cycle, which minimizes changes to existing drivers. Note that the Intel code will be modified in follow-up patches (no impact on any platform since the connection with ASoC is not supported upstream so far). Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Acked-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20200518174322.31561-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/Makefile | 2 +- drivers/soundwire/bus.c | 14 ++++++-- drivers/soundwire/bus.h | 3 ++ drivers/soundwire/intel.c | 1 - drivers/soundwire/master.c | 81 +++++++++++++++++++++++++++++++++++++++++++ drivers/soundwire/qcom.c | 1 - include/linux/soundwire/sdw.h | 17 ++++++++- 7 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 drivers/soundwire/master.c diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index e2cdff990e9f..7319918e0aec 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,7 +4,7 @@ # #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 master.o slave.o mipi_disco.o stream.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o ifdef CONFIG_DEBUG_FS diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 03d72fa3c190..11cfe435e4a8 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -37,14 +37,21 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, struct sdw_master_prop *prop = NULL; int ret; - if (!bus->dev) { - pr_err("SoundWire bus has no device\n"); + if (!parent) { + pr_err("SoundWire parent device is not set\n"); return -ENODEV; } ret = sdw_get_id(bus); if (ret) { - dev_err(bus->dev, "Failed to get bus id\n"); + dev_err(parent, "Failed to get bus id\n"); + return ret; + } + + ret = sdw_master_device_add(bus, parent, fwnode); + if (ret) { + dev_err(parent, "Failed to add master device at link %d\n", + bus->link_id); return ret; } @@ -161,6 +168,7 @@ static int sdw_delete_slave(struct device *dev, void *data) void sdw_bus_master_delete(struct sdw_bus *bus) { device_for_each_child(bus->dev, NULL, sdw_delete_slave); + sdw_master_device_del(bus); sdw_bus_debugfs_exit(bus); ida_free(&sdw_ida, bus->id); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 204204a26db8..93ab0234a491 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -19,6 +19,9 @@ static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) int sdw_of_find_slaves(struct sdw_bus *bus); void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id); +int sdw_master_device_add(struct sdw_bus *bus, struct device *parent, + struct fwnode_handle *fwnode); +int sdw_master_device_del(struct sdw_bus *bus); #ifdef CONFIG_DEBUG_FS void sdw_bus_debugfs_init(struct sdw_bus *bus); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 430f8addef83..4cfdd074e310 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1099,7 +1099,6 @@ static int intel_probe(struct platform_device *pdev) sdw->cdns.registers = sdw->link_res->registers; sdw->cdns.instance = sdw->instance; sdw->cdns.msg_count = 0; - sdw->cdns.bus.dev = &pdev->dev; sdw->cdns.bus.link_id = pdev->id; sdw_cdns_probe(&sdw->cdns); diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c new file mode 100644 index 000000000000..6be0a027def7 --- /dev/null +++ b/drivers/soundwire/master.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2019-2020 Intel Corporation. + +#include +#include +#include +#include +#include "bus.h" + +static void sdw_master_device_release(struct device *dev) +{ + struct sdw_master_device *md = dev_to_sdw_master_device(dev); + + kfree(md); +} + +struct device_type sdw_master_type = { + .name = "soundwire_master", + .release = sdw_master_device_release, +}; + +/** + * 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.of_node = parent->of_node; + md->dev.fwnode = fwnode; + md->dev.dma_mask = parent->dma_mask; + + dev_set_name(&md->dev, "sdw-master-%d", bus->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; + +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) +{ + device_unregister(bus->dev); + + return 0; +} diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index ea03bb988f81..a1c2a44a3b4d 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -784,7 +784,6 @@ static int qcom_swrm_probe(struct platform_device *pdev) mutex_init(&ctrl->port_lock); INIT_WORK(&ctrl->slave_work, qcom_swrm_slave_wq); - ctrl->bus.dev = dev; ctrl->bus.ops = &qcom_swrm_ops; ctrl->bus.port_ops = &qcom_swrm_port_ops; ctrl->bus.compute_params = &qcom_swrm_compute_params; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a32cb26f1815..7658d9698dd5 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -632,6 +632,19 @@ struct sdw_slave { #define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev) +/** + * struct sdw_master_device - SoundWire 'Master Device' representation + * @dev: Linux device for this Master + * @bus: Bus handle shortcut + */ +struct sdw_master_device { + struct device dev; + struct sdw_bus *bus; +}; + +#define dev_to_sdw_master_device(d) \ + container_of(d, struct sdw_master_device, dev) + struct sdw_driver { const char *name; @@ -787,7 +800,8 @@ struct sdw_master_ops { /** * struct sdw_bus - SoundWire bus - * @dev: Master linux device + * @dev: Shortcut to &bus->md->dev to avoid changing the entire code. + * @md: Master device * @link_id: Link id number, can be 0 to N, unique for each Master * @id: bus system-wide unique id * @slaves: list of Slaves on this bus @@ -813,6 +827,7 @@ struct sdw_master_ops { */ struct sdw_bus { struct device *dev; + struct sdw_master_device *md; unsigned int link_id; int id; struct list_head slaves; -- cgit v1.2.3 From 26d970225d876437287ec47a60cf4a07fe08733d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 19 May 2020 01:43:22 +0800 Subject: soundwire: master: add runtime pm support We need to enable runtime_pm on master device with generic helpers, so that a Slave-initiated wake is propagated to the bus parent. Signed-off-by: Bard Liao Acked-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20200518174322.31561-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/master.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c index 6be0a027def7..5411791e6aff 100644 --- a/drivers/soundwire/master.c +++ b/drivers/soundwire/master.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include "bus.h" @@ -14,9 +15,15 @@ static void sdw_master_device_release(struct 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, }; /** -- cgit v1.2.3 From 6bf393c577c4a6e324ab103425fbf71126e5385b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 May 2020 04:35:49 +0800 Subject: soundwire: disco: s/ch/channels/ Use more meaningful member names in preparation for sysfs support. No functionality change. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200518203551.2053-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/mipi_disco.c | 11 ++++++----- include/linux/soundwire/sdw.h | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c index 844e6b22974f..4ae62b452b8c 100644 --- a/drivers/soundwire/mipi_disco.c +++ b/drivers/soundwire/mipi_disco.c @@ -231,16 +231,17 @@ static int sdw_slave_read_dpn(struct sdw_slave *slave, nval = fwnode_property_count_u32(node, "mipi-sdw-channel-number-list"); if (nval > 0) { - dpn[i].num_ch = nval; - dpn[i].ch = devm_kcalloc(&slave->dev, dpn[i].num_ch, - sizeof(*dpn[i].ch), + dpn[i].num_channels = nval; + dpn[i].channels = devm_kcalloc(&slave->dev, + dpn[i].num_channels, + sizeof(*dpn[i].channels), GFP_KERNEL); - if (!dpn[i].ch) + if (!dpn[i].channels) return -ENOMEM; fwnode_property_read_u32_array(node, "mipi-sdw-channel-number-list", - dpn[i].ch, dpn[i].num_ch); + dpn[i].channels, dpn[i].num_channels); } nval = fwnode_property_count_u32(node, "mipi-sdw-channel-combination-list"); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 7658d9698dd5..9c27a32df9bb 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -291,8 +291,8 @@ struct sdw_dpn_audio_mode { * implementation-defined interrupts * @max_ch: Maximum channels supported * @min_ch: Minimum channels supported - * @num_ch: Number of discrete channels supported - * @ch: Discrete channels supported + * @num_channels: Number of discrete channels supported + * @channels: Discrete channels supported * @num_ch_combinations: Number of channel combinations supported * @ch_combinations: Channel combinations supported * @modes: SDW mode supported @@ -316,8 +316,8 @@ struct sdw_dpn_prop { u32 imp_def_interrupts; u32 max_ch; u32 min_ch; - u32 num_ch; - u32 *ch; + u32 num_channels; + u32 *channels; u32 num_ch_combinations; u32 *ch_combinations; u32 modes; -- cgit v1.2.3 From c5778ca49a19420c67dbeff0744a3b3b75ef4e1a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 19 May 2020 04:35:50 +0800 Subject: soundwire: master: add sysfs support Add the master properties as attributes. The description is directly derived from the MIPI DisCo specification. 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 Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200518203551.2053-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- .../ABI/testing/sysfs-bus-soundwire-master | 23 ++++++ drivers/soundwire/master.c | 84 ++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-soundwire-master diff --git a/Documentation/ABI/testing/sysfs-bus-soundwire-master b/Documentation/ABI/testing/sysfs-bus-soundwire-master new file mode 100644 index 000000000000..46ef038d8722 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-soundwire-master @@ -0,0 +1,23 @@ +What: /sys/bus/soundwire/devices/sdw-master-N/revision + /sys/bus/soundwire/devices/sdw-master-N/clk_stop_modes + /sys/bus/soundwire/devices/sdw-master-N/clk_freq + /sys/bus/soundwire/devices/sdw-master-N/clk_gears + /sys/bus/soundwire/devices/sdw-master-N/default_col + /sys/bus/soundwire/devices/sdw-master-N/default_frame_rate + /sys/bus/soundwire/devices/sdw-master-N/default_row + /sys/bus/soundwire/devices/sdw-master-N/dynamic_shape + /sys/bus/soundwire/devices/sdw-master-N/err_threshold + /sys/bus/soundwire/devices/sdw-master-N/max_clk_freq + +Date: April 2020 + +Contact: Pierre-Louis Bossart + Bard Liao + Vinod Koul + +Description: SoundWire Master-N DisCo properties. + These properties are defined by MIPI DisCo Specification + for SoundWire. They define various properties of the Master + and are used by the bus to configure the Master. clk_stop_modes + is a bitmask for simplifications and combines the + clock-stop-mode0 and clock-stop-mode1 properties. diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c index 5411791e6aff..5f0b2189defe 100644 --- a/drivers/soundwire/master.c +++ b/drivers/soundwire/master.c @@ -8,6 +8,89 @@ #include #include "bus.h" +/* + * 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); @@ -48,6 +131,7 @@ int sdw_master_device_add(struct sdw_bus *bus, struct device *parent, 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; -- cgit v1.2.3 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 --- .../ABI/testing/sysfs-bus-soundwire-slave | 91 +++++++ drivers/soundwire/Makefile | 3 +- drivers/soundwire/bus.c | 1 + drivers/soundwire/bus.h | 1 + drivers/soundwire/bus_type.c | 9 +- drivers/soundwire/sysfs_local.h | 14 + drivers/soundwire/sysfs_slave.c | 215 +++++++++++++++ drivers/soundwire/sysfs_slave_dpn.c | 300 +++++++++++++++++++++ 8 files changed, 631 insertions(+), 3 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-soundwire-slave create mode 100644 drivers/soundwire/sysfs_local.h create mode 100644 drivers/soundwire/sysfs_slave.c create mode 100644 drivers/soundwire/sysfs_slave_dpn.c diff --git a/Documentation/ABI/testing/sysfs-bus-soundwire-slave b/Documentation/ABI/testing/sysfs-bus-soundwire-slave new file mode 100644 index 000000000000..db4c9511d1aa --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-soundwire-slave @@ -0,0 +1,91 @@ +What: /sys/bus/soundwire/devices/sdw:.../dev-properties/mipi_revision + /sys/bus/soundwire/devices/sdw:.../dev-properties/wake_capable + /sys/bus/soundwire/devices/sdw:.../dev-properties/test_mode_capable + /sys/bus/soundwire/devices/sdw:.../dev-properties/clk_stop_mode1 + /sys/bus/soundwire/devices/sdw:.../dev-properties/simple_clk_stop_capable + /sys/bus/soundwire/devices/sdw:.../dev-properties/clk_stop_timeout + /sys/bus/soundwire/devices/sdw:.../dev-properties/ch_prep_timeout + /sys/bus/soundwire/devices/sdw:.../dev-properties/reset_behave + /sys/bus/soundwire/devices/sdw:.../dev-properties/high_PHY_capable + /sys/bus/soundwire/devices/sdw:.../dev-properties/paging_support + /sys/bus/soundwire/devices/sdw:.../dev-properties/bank_delay_support + /sys/bus/soundwire/devices/sdw:.../dev-properties/p15_behave + /sys/bus/soundwire/devices/sdw:.../dev-properties/master_count + /sys/bus/soundwire/devices/sdw:.../dev-properties/source_ports + /sys/bus/soundwire/devices/sdw:.../dev-properties/sink_ports + +Date: May 2020 + +Contact: Pierre-Louis Bossart + Bard Liao + Vinod Koul + +Description: SoundWire Slave DisCo properties. + These properties are defined by MIPI DisCo Specification + for SoundWire. They define various properties of the + SoundWire Slave and are used by the bus to configure + the Slave + + +What: /sys/bus/soundwire/devices/sdw:.../dp0/max_word + /sys/bus/soundwire/devices/sdw:.../dp0/min_word + /sys/bus/soundwire/devices/sdw:.../dp0/words + /sys/bus/soundwire/devices/sdw:.../dp0/BRA_flow_controlled + /sys/bus/soundwire/devices/sdw:.../dp0/simple_ch_prep_sm + /sys/bus/soundwire/devices/sdw:.../dp0/imp_def_interrupts + +Date: May 2020 + +Contact: Pierre-Louis Bossart + Bard Liao + Vinod Koul + +Description: SoundWire Slave Data Port-0 DisCo properties. + These properties are defined by MIPI DisCo Specification + for the SoundWire. They define various properties of the + Data port 0 are used by the bus to configure the Data Port 0. + + +What: /sys/bus/soundwire/devices/sdw:.../dpN_src/max_word + /sys/bus/soundwire/devices/sdw:.../dpN_src/min_word + /sys/bus/soundwire/devices/sdw:.../dpN_src/words + /sys/bus/soundwire/devices/sdw:.../dpN_src/type + /sys/bus/soundwire/devices/sdw:.../dpN_src/max_grouping + /sys/bus/soundwire/devices/sdw:.../dpN_src/simple_ch_prep_sm + /sys/bus/soundwire/devices/sdw:.../dpN_src/ch_prep_timeout + /sys/bus/soundwire/devices/sdw:.../dpN_src/imp_def_interrupts + /sys/bus/soundwire/devices/sdw:.../dpN_src/min_ch + /sys/bus/soundwire/devices/sdw:.../dpN_src/max_ch + /sys/bus/soundwire/devices/sdw:.../dpN_src/channels + /sys/bus/soundwire/devices/sdw:.../dpN_src/ch_combinations + /sys/bus/soundwire/devices/sdw:.../dpN_src/max_async_buffer + /sys/bus/soundwire/devices/sdw:.../dpN_src/block_pack_mode + /sys/bus/soundwire/devices/sdw:.../dpN_src/port_encoding + + /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_word + /sys/bus/soundwire/devices/sdw:.../dpN_sink/min_word + /sys/bus/soundwire/devices/sdw:.../dpN_sink/words + /sys/bus/soundwire/devices/sdw:.../dpN_sink/type + /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_grouping + /sys/bus/soundwire/devices/sdw:.../dpN_sink/simple_ch_prep_sm + /sys/bus/soundwire/devices/sdw:.../dpN_sink/ch_prep_timeout + /sys/bus/soundwire/devices/sdw:.../dpN_sink/imp_def_interrupts + /sys/bus/soundwire/devices/sdw:.../dpN_sink/min_ch + /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_ch + /sys/bus/soundwire/devices/sdw:.../dpN_sink/channels + /sys/bus/soundwire/devices/sdw:.../dpN_sink/ch_combinations + /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_async_buffer + /sys/bus/soundwire/devices/sdw:.../dpN_sink/block_pack_mode + /sys/bus/soundwire/devices/sdw:.../dpN_sink/port_encoding + +Date: May 2020 + +Contact: Pierre-Louis Bossart + Bard Liao + Vinod Koul + +Description: SoundWire Slave Data Source/Sink Port-N DisCo properties. + These properties are defined by MIPI DisCo Specification + for SoundWire. They define various properties of the + Source/Sink Data port N and are used by the bus to configure + the Data Port N. diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 7319918e0aec..9b0aeee94876 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,7 +4,8 @@ # #Bus Objs -soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o +soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o \ + sysfs_slave.o sysfs_slave_dpn.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o ifdef CONFIG_DEBUG_FS diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 11cfe435e4a8..24ba77226376 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -8,6 +8,7 @@ #include #include #include "bus.h" +#include "sysfs_local.h" static DEFINE_IDA(sdw_ida); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 93ab0234a491..82484f741168 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -175,5 +175,6 @@ sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) #define SDW_UNATTACH_REQUEST_MASTER_RESET BIT(0) void sdw_clear_slave_status(struct sdw_bus *bus, u32 request); +int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size); #endif /* __SDW_BUS_H */ diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 2c1a19caba51..de9a671802b8 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -7,6 +7,7 @@ #include #include #include "bus.h" +#include "sysfs_local.h" /** * sdw_get_device_id - find the matching SoundWire device id @@ -46,8 +47,7 @@ static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) return ret; } -static int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, - size_t size) +int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) { /* modalias is sdw:mp */ @@ -105,6 +105,11 @@ static int sdw_drv_probe(struct device *dev) if (slave->ops && slave->ops->read_prop) slave->ops->read_prop(slave); + /* init the sysfs as we have properties now */ + ret = sdw_slave_sysfs_init(slave); + if (ret < 0) + dev_warn(dev, "Slave sysfs init failed:%d\n", ret); + /* * Check for valid clk_stop_timeout, use DisCo worst case value of * 300ms diff --git a/drivers/soundwire/sysfs_local.h b/drivers/soundwire/sysfs_local.h new file mode 100644 index 000000000000..ff60adee3c41 --- /dev/null +++ b/drivers/soundwire/sysfs_local.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2015-2020 Intel Corporation. */ + +#ifndef __SDW_SYSFS_LOCAL_H +#define __SDW_SYSFS_LOCAL_H + +/* + * SDW sysfs APIs - + */ + +int sdw_slave_sysfs_init(struct sdw_slave *slave); +int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave); + +#endif /* __SDW_SYSFS_LOCAL_H */ 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; +} + diff --git a/drivers/soundwire/sysfs_slave_dpn.c b/drivers/soundwire/sysfs_slave_dpn.c new file mode 100644 index 000000000000..c175a7d08815 --- /dev/null +++ b/drivers/soundwire/sysfs_slave_dpn.c @@ -0,0 +1,300 @@ +// 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" + +struct dpn_attribute { + struct device_attribute dev_attr; + int N; + int dir; + const char *format_string; +}; + +/* + * Since we can't use ARRAY_SIZE, hard-code number of dpN attributes. + * This needs to be updated when adding new attributes - an error will be + * flagged on a mismatch. + */ +#define SDW_DPN_ATTRIBUTES 15 + +#define sdw_dpn_attribute_alloc(field) \ +static int field##_attribute_alloc(struct device *dev, \ + struct attribute **res, \ + int N, int dir, \ + const char *format_string) \ +{ \ + struct dpn_attribute *dpn_attr; \ + \ + dpn_attr = devm_kzalloc(dev, sizeof(*dpn_attr), GFP_KERNEL); \ + if (!dpn_attr) \ + return -ENOMEM; \ + dpn_attr->N = N; \ + dpn_attr->dir = dir; \ + dpn_attr->format_string = format_string; \ + dpn_attr->dev_attr.attr.name = __stringify(field); \ + dpn_attr->dev_attr.attr.mode = 0444; \ + dpn_attr->dev_attr.show = field##_show; \ + \ + *res = &dpn_attr->dev_attr.attr; \ + \ + return 0; \ +} + +#define sdw_dpn_attr(field) \ + \ +static ssize_t field##_dpn_show(struct sdw_slave *slave, \ + int N, \ + int dir, \ + const char *format_string, \ + char *buf) \ +{ \ + struct sdw_dpn_prop *dpn; \ + unsigned long mask; \ + int bit; \ + int i; \ + \ + if (dir) { \ + dpn = slave->prop.src_dpn_prop; \ + mask = slave->prop.source_ports; \ + } else { \ + dpn = slave->prop.sink_dpn_prop; \ + mask = slave->prop.sink_ports; \ + } \ + \ + i = 0; \ + for_each_set_bit(bit, &mask, 32) { \ + if (bit == N) { \ + return sprintf(buf, format_string, \ + dpn[i].field); \ + } \ + i++; \ + } \ + return -EINVAL; \ +} \ + \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct sdw_slave *slave = dev_to_sdw_dev(dev); \ + struct dpn_attribute *dpn_attr = \ + container_of(attr, struct dpn_attribute, dev_attr); \ + \ + return field##_dpn_show(slave, \ + dpn_attr->N, dpn_attr->dir, \ + dpn_attr->format_string, \ + buf); \ +} \ +sdw_dpn_attribute_alloc(field) + +sdw_dpn_attr(imp_def_interrupts); +sdw_dpn_attr(max_word); +sdw_dpn_attr(min_word); +sdw_dpn_attr(type); +sdw_dpn_attr(max_grouping); +sdw_dpn_attr(simple_ch_prep_sm); +sdw_dpn_attr(ch_prep_timeout); +sdw_dpn_attr(max_ch); +sdw_dpn_attr(min_ch); +sdw_dpn_attr(max_async_buffer); +sdw_dpn_attr(block_pack_mode); +sdw_dpn_attr(port_encoding); + +#define sdw_dpn_array_attr(field) \ + \ +static ssize_t field##_dpn_show(struct sdw_slave *slave, \ + int N, \ + int dir, \ + const char *format_string, \ + char *buf) \ +{ \ + struct sdw_dpn_prop *dpn; \ + unsigned long mask; \ + ssize_t size = 0; \ + int bit; \ + int i; \ + int j; \ + \ + if (dir) { \ + dpn = slave->prop.src_dpn_prop; \ + mask = slave->prop.source_ports; \ + } else { \ + dpn = slave->prop.sink_dpn_prop; \ + mask = slave->prop.sink_ports; \ + } \ + \ + i = 0; \ + for_each_set_bit(bit, &mask, 32) { \ + if (bit == N) { \ + for (j = 0; j < dpn[i].num_##field; j++) \ + size += sprintf(buf + size, \ + format_string, \ + dpn[i].field[j]); \ + size += sprintf(buf + size, "\n"); \ + return size; \ + } \ + i++; \ + } \ + return -EINVAL; \ +} \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct sdw_slave *slave = dev_to_sdw_dev(dev); \ + struct dpn_attribute *dpn_attr = \ + container_of(attr, struct dpn_attribute, dev_attr); \ + \ + return field##_dpn_show(slave, \ + dpn_attr->N, dpn_attr->dir, \ + dpn_attr->format_string, \ + buf); \ +} \ +sdw_dpn_attribute_alloc(field) + +sdw_dpn_array_attr(words); +sdw_dpn_array_attr(ch_combinations); +sdw_dpn_array_attr(channels); + +static int add_all_attributes(struct device *dev, int N, int dir) +{ + struct attribute **dpn_attrs; + struct attribute_group *dpn_group; + int i = 0; + int ret; + + /* allocate attributes, last one is NULL */ + dpn_attrs = devm_kcalloc(dev, SDW_DPN_ATTRIBUTES + 1, + sizeof(struct attribute *), + GFP_KERNEL); + if (!dpn_attrs) + return -ENOMEM; + + ret = max_word_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = min_word_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = words_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = type_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = max_grouping_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = simple_ch_prep_sm_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = ch_prep_timeout_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = imp_def_interrupts_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "0x%x\n"); + if (ret < 0) + return ret; + + ret = min_ch_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = max_ch_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = channels_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = ch_combinations_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = max_async_buffer_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = block_pack_mode_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + ret = port_encoding_attribute_alloc(dev, &dpn_attrs[i++], + N, dir, "%d\n"); + if (ret < 0) + return ret; + + /* paranioa check for editing mistakes */ + if (i != SDW_DPN_ATTRIBUTES) { + dev_err(dev, "mismatch in attributes, allocated %d got %d\n", + SDW_DPN_ATTRIBUTES, i); + return -EINVAL; + } + + dpn_group = devm_kzalloc(dev, sizeof(*dpn_group), GFP_KERNEL); + if (!dpn_group) + return -ENOMEM; + + dpn_group->attrs = dpn_attrs; + dpn_group->name = devm_kasprintf(dev, GFP_KERNEL, "dp%d_%s", + N, dir ? "src" : "sink"); + if (!dpn_group->name) + return -ENOMEM; + + ret = devm_device_add_group(dev, dpn_group); + if (ret < 0) + return ret; + + return 0; +} + +int sdw_slave_sysfs_dpn_init(struct sdw_slave *slave) +{ + unsigned long mask; + int ret; + int i; + + mask = slave->prop.source_ports; + for_each_set_bit(i, &mask, 32) { + ret = add_all_attributes(&slave->dev, i, 1); + if (ret < 0) + return ret; + } + + mask = slave->prop.sink_ports; + for_each_set_bit(i, &mask, 32) { + ret = add_all_attributes(&slave->dev, i, 0); + if (ret < 0) + return ret; + } + + return 0; +} -- cgit v1.2.3 From 7541765df14b2a705cafbe46632069a4f71af1c4 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 20 May 2020 17:27:45 +0530 Subject: soundwire: fix trailing line in sysfs_slave.c Commit bcac59029955 ("soundwire: add Slave sysfs support") added trailing line in file sysfs_slave.c, so remove it Fixes: bcac59029955 ("soundwire: add Slave sysfs support") Signed-off-by: Vinod Koul --- drivers/soundwire/sysfs_slave.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/soundwire/sysfs_slave.c b/drivers/soundwire/sysfs_slave.c index ae6d241eca50..f510071b0add 100644 --- a/drivers/soundwire/sysfs_slave.c +++ b/drivers/soundwire/sysfs_slave.c @@ -212,4 +212,3 @@ int sdw_slave_sysfs_init(struct sdw_slave *slave) return 0; } - -- cgit v1.2.3 From 84091304a48c6d86a910b5a7d187592097da150f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 20 May 2020 17:27:45 +0530 Subject: soundwire: fix spelling mistake Fix typo for paranoia spelled as paranioa Fixes: bcac59029955 ("soundwire: add Slave sysfs support") Signed-off-by: Vinod Koul --- drivers/soundwire/sysfs_slave_dpn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/sysfs_slave_dpn.c b/drivers/soundwire/sysfs_slave_dpn.c index c175a7d08815..05a721ea9830 100644 --- a/drivers/soundwire/sysfs_slave_dpn.c +++ b/drivers/soundwire/sysfs_slave_dpn.c @@ -252,7 +252,7 @@ static int add_all_attributes(struct device *dev, int N, int dir) if (ret < 0) return ret; - /* paranioa check for editing mistakes */ + /* paranoia check for editing mistakes */ if (i != SDW_DPN_ATTRIBUTES) { dev_err(dev, "mismatch in attributes, allocated %d got %d\n", SDW_DPN_ATTRIBUTES, i); -- cgit v1.2.3 From fcbcf1f7b56855ffe1cec768cb1013c5a4c854c3 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 20 May 2020 03:19:02 +0800 Subject: soundwire: intel: use a single module It's not clear why we have two modules for the Intel controller/master support when there is a single Kconfig. This adds complexity for no good reason, the two parts need to work together anyways. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200519191903.6557-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/Makefile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 9b0aeee94876..b5871612613b 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -17,12 +17,9 @@ soundwire-cadence-objs := cadence_master.o obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o #Intel driver -soundwire-intel-objs := intel.o +soundwire-intel-objs := intel.o intel_init.o obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o -soundwire-intel-init-objs := intel_init.o -obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel-init.o - #Qualcomm driver soundwire-qcom-objs := qcom.o obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o -- cgit v1.2.3