Commit ddc0aef0 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'soundwire-5.8-rc1' of...

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

Vinod writes:

soundwire updates for v5.8-rc1

This contains sdw_master_device patches and other updates done by Intel
folks.

Details:
  - sdw_master_device to represent the master instances.
  - sysfs properties for sdw_master_device and sdw_slave.
  - Documentation update for TDM modes.
  - some code cleanup patches and odd updates.

* tag 'soundwire-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
  soundwire: intel: use a single module
  soundwire: fix spelling mistake
  soundwire: fix trailing line in sysfs_slave.c
  soundwire: add Slave sysfs support
  soundwire: master: add sysfs support
  soundwire: disco: s/ch/channels/
  soundwire: master: add runtime pm support
  soundwire: bus_type: add sdw_master_device support
  soundwire: bus: add unique bus id
  soundwire: bus_type: introduce sdw_slave_type and sdw_master_type
  soundwire: bus: rename sdw_bus_master_add/delete, add arguments
  soundwire: intel: (cosmetic) remove multiple superfluous "else" statements
  soundwire: (cosmetic) remove multiple superfluous "else" statements
  soundwire: qcom: Use IRQF_ONESHOT
  soundwire: bus: reduce verbosity on enumeration
  soundwire: debugfs: clarify SDPX license with GPL-2.0-only
  soundwire: slave: don't init debugfs on device registration error
  Documentation: SoundWire: clarify TDM mode support
  soundwire: qcom: fix error handling in probe
  soundwire: intel: use asoc_rtd_to_cpu() / asoc_rtd_to_codec() macro for DAI pointer
parents c9cf27d9 fcbcf1f7
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 <pierre-louis.bossart@linux.intel.com>
Bard Liao <yung-chuan.liao@linux.intel.com>
Vinod Koul <vkoul@kernel.org>
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.
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 <pierre-louis.bossart@linux.intel.com>
Bard Liao <yung-chuan.liao@linux.intel.com>
Vinod Koul <vkoul@kernel.org>
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 <pierre-louis.bossart@linux.intel.com>
Bard Liao <yung-chuan.liao@linux.intel.com>
Vinod Koul <vkoul@kernel.org>
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 <pierre-louis.bossart@linux.intel.com>
Bard Liao <yung-chuan.liao@linux.intel.com>
Vinod Koul <vkoul@kernel.org>
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.
......@@ -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
================================
......
......@@ -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);
......
......@@ -4,7 +4,8 @@
#
#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 \
sysfs_slave.o sysfs_slave_dpn.o
obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o
ifdef CONFIG_DEBUG_FS
......@@ -16,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
......@@ -8,24 +8,54 @@
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include "bus.h"
#include "sysfs_local.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_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;
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(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;
}
if (!bus->ops) {
dev_err(bus->dev, "SoundWire Bus ops are not set\n");
return -EINVAL;
......@@ -107,7 +137,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 +161,20 @@ 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_master_device_del(bus);
sdw_bus_debugfs_exit(bus);
ida_free(&sdw_ida, bus->id);
}
EXPORT_SYMBOL(sdw_delete_bus_master);
EXPORT_SYMBOL(sdw_bus_master_delete);
/*
* SDW IO Calls
......@@ -284,9 +316,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 +339,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 +410,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 +506,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);
......@@ -563,9 +598,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;
......
......@@ -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);
......@@ -172,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 */
......@@ -7,6 +7,7 @@
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "bus.h"
#include "sysfs_local.h"
/**
* sdw_get_device_id - find the matching SoundWire device id
......@@ -33,10 +34,17 @@ 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)
......@@ -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);
......@@ -98,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
......
......@@ -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;
......
// SPDX-License-Identifier: GPL-2.0
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2017-2019 Intel Corporation.
#include <linux/device.h>
......
......@@ -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;
}
}
......@@ -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);
......@@ -1110,9 +1109,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 +1172,7 @@ static int intel_probe(struct platform_device *pdev)
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 +1188,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;
}
......
......@@ -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;
}
......
// 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 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", 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;
}
......@@ -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");
......
......@@ -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);
......@@ -780,14 +784,13 @@ 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;
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;
......@@ -810,32 +813,37 @@ 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");
goto err;
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);
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_bus_master_delete(&ctrl->bus);
err_clk:
clk_disable_unprepare(ctrl->hclk);
err_init:
return ret;
}
......@@ -843,7 +851,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;
......
......@@ -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);
......@@ -68,6 +74,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);
......
/* 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 */
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2015-2020 Intel Corporation.
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#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_<sink/src>
* |---- 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;
}
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2015-2020 Intel Corporation.
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#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;
/* 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);
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;
}
......@@ -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;
......@@ -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,8 +800,10 @@ 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
* @assigned: Bitmap for Slave device numbers.
* Bit set implies used number, bit clear implies unused number.
......@@ -812,7 +827,9 @@ 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;
DECLARE_BITMAP(assigned, SDW_MAX_DEVICES);
struct mutex bus_lock;
......@@ -832,8 +849,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
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment