This patch adds following SoundWire bus driver APIs.

    1. Register SoundWire Master device and driver.
    2. Register SoundWire Slave driver.
    3. Register Slave device capabilities.

Signed-off-by: Hardik Shah <[email protected]>
Signed-off-by: Sanyog Kale <[email protected]>
Reviewed-by: Pierre-Louis Bossart <[email protected]>
---
 sound/Kconfig        |    2 +
 sound/Makefile       |    1 +
 sound/sdw/Kconfig    |    6 +
 sound/sdw/Makefile   |    1 +
 sound/sdw/sdw.c      |  886 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sound/sdw/sdw_priv.h |  102 ++++++
 6 files changed, 998 insertions(+)
 create mode 100644 sound/sdw/Kconfig
 create mode 100644 sound/sdw/Makefile
 create mode 100644 sound/sdw/sdw.c
 create mode 100644 sound/sdw/sdw_priv.h

diff --git a/sound/Kconfig b/sound/Kconfig
index 5a240e0..9f67cf7 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -108,6 +108,8 @@ source "sound/parisc/Kconfig"
 
 source "sound/soc/Kconfig"
 
+source "sound/sdw/Kconfig"
+
 endif # SND
 
 menuconfig SOUND_PRIME
diff --git a/sound/Makefile b/sound/Makefile
index c41bdf5..914101e 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -3,6 +3,7 @@
 
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
+obj-$(CONFIG_SOUND_SDW) += sdw/
 obj-$(CONFIG_DMASOUND) += oss/
 obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
        firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/
diff --git a/sound/sdw/Kconfig b/sound/sdw/Kconfig
new file mode 100644
index 0000000..052079f
--- /dev/null
+++ b/sound/sdw/Kconfig
@@ -0,0 +1,6 @@
+config SOUND_SDW
+       tristate
+       help
+         SoundWire interface is typically used for transporting data
+         related to audio functions. Concerned drivers should "select"
+         this.
diff --git a/sound/sdw/Makefile b/sound/sdw/Makefile
new file mode 100644
index 0000000..6ed1881
--- /dev/null
+++ b/sound/sdw/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOUND_SDW)                       += sdw.o
diff --git a/sound/sdw/sdw.c b/sound/sdw/sdw.c
new file mode 100644
index 0000000..d4e79b8a
--- /dev/null
+++ b/sound/sdw/sdw.c
@@ -0,0 +1,886 @@
+/*
+ * sdw.c - SoundWire bus driver implementation.
+ *
+ * Author: Hardik Shah <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
+#include <sound/sdw_bus.h>
+#include <sound/sdw_master.h>
+#include <sound/sdw_slave.h>
+
+#include "sdw_priv.h"
+
+/*
+ * Global SoundWire core instance contains list of Masters registered, core
+ *     lock and SoundWire stream tags.
+ */
+struct snd_sdw_core snd_sdw_core;
+
+static void sdw_slv_release(struct device *dev)
+{
+       kfree(to_sdw_slave(dev));
+}
+
+static void sdw_mstr_release(struct device *dev)
+{
+       struct sdw_master *mstr = to_sdw_master(dev);
+
+       complete(&mstr->slv_released_complete);
+}
+
+static struct device_type sdw_slv_type = {
+       .groups         = NULL,
+       .release        = sdw_slv_release,
+};
+
+static struct device_type sdw_mstr_type = {
+       .groups         = NULL,
+       .release        = sdw_mstr_release,
+};
+
+/**
+ * sdw_slv_verify - return parameter as sdw_slave, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * When traversing the driver model tree, perhaps using driver model
+ * iterators like @device_for_each_child(), you can't assume very much
+ * about the nodes you find. Use this function to avoid oopses caused
+ * by wrongly treating some non-SDW device as an sdw_slave.
+ */
+static struct sdw_slave *sdw_slv_verify(struct device *dev)
+{
+       return (dev->type == &sdw_slv_type)
+                       ? to_sdw_slave(dev)
+                       : NULL;
+}
+
+/**
+ * sdw_mstr_verify: return parameter as sdw_master, or NULL
+ *
+ * @dev: device, probably from some driver model iterator
+ *
+ * When traversing the driver model tree, perhaps using driver model
+ * iterators like @device_for_each_child(), you can't assume very much
+ * about the nodes you find. Use this function to avoid oopses caused
+ * by wrongly treating some non-SDW device as an sdw_master.
+ */
+static struct sdw_master *sdw_mstr_verify(struct device *dev)
+{
+       return (dev->type == &sdw_mstr_type)
+                       ? to_sdw_master(dev)
+                       : NULL;
+}
+
+static const struct sdw_slave_id *sdw_match_slv(const struct sdw_slave_id *id,
+                                       const struct sdw_slave *sdw_slv)
+{
+       const struct sdw_slave_priv *slv_priv = &sdw_slv->priv;
+
+       if (!id)
+               return NULL;
+
+       /*
+        * IDs should be NULL terminated like the last ID in the list should
+        * be null, as done for drivers like platform, i2c etc.
+        */
+       while (id->name[0]) {
+               if (strncmp(slv_priv->name, id->name, SOUNDWIRE_NAME_SIZE) == 0)
+                       return id;
+
+               id++;
+       }
+
+       return NULL;
+}
+
+static const struct sdw_master_id *sdw_match_mstr(
+                       const struct sdw_master_id *id,
+                       const struct sdw_master *sdw_mstr)
+{
+       if (!id)
+               return NULL;
+
+       /*
+        * IDs should be NULL terminated like the last ID in the list should
+        * be null, as done for drivers like platform, i2c etc.
+        */
+       while (id->name[0]) {
+               if (strncmp(sdw_mstr->name, id->name, SOUNDWIRE_NAME_SIZE) == 0)
+                       return id;
+               id++;
+       }
+       return NULL;
+}
+
+static int sdw_slv_match(struct device *dev, struct device_driver *driver)
+{
+       struct sdw_slave *sdw_slv;
+       struct sdw_driver *sdw_drv = to_sdw_driver(driver);
+       struct sdw_slave_driver *drv;
+       int ret = 0;
+
+
+       if (sdw_drv->driver_type != SDW_DRIVER_TYPE_SLAVE)
+               return ret;
+
+       drv = to_sdw_slave_driver(driver);
+       sdw_slv = to_sdw_slave(dev);
+
+       /*
+        * We are matching based on the dev_id field, dev_id field is unique
+        * based on part_id and manufacturer id. Device will be registered
+        * based on dev_id and driver will also have same dev_id for device
+        * its controlling.
+        */
+       ret = (sdw_match_slv(drv->id_table, sdw_slv) != NULL);
+
+       if (ret < 0)
+               sdw_slv->priv.driver = drv;
+
+       return ret;
+}
+
+static int sdw_mstr_match(struct device *dev, struct device_driver *driver)
+{
+       struct sdw_master *sdw_mstr;
+       struct sdw_driver *sdw_drv = to_sdw_driver(driver);
+       struct sdw_master_driver *drv;
+       int ret = 0;
+
+       if (sdw_drv->driver_type != SDW_DRIVER_TYPE_MASTER)
+               return ret;
+
+       drv = to_sdw_master_driver(driver);
+       sdw_mstr = to_sdw_master(dev);
+
+       ret = (sdw_match_mstr(drv->id_table, sdw_mstr) != NULL);
+
+       if (driver->name && !ret)
+               ret = (strncmp(sdw_mstr->name, driver->name,
+                       SOUNDWIRE_NAME_SIZE) == 0);
+
+       if (ret < 0)
+               sdw_mstr->driver = drv;
+
+       return ret;
+}
+
+static int sdw_mstr_probe(struct device *dev)
+{
+       const struct sdw_master_driver *sdrv =
+                                       to_sdw_master_driver(dev->driver);
+       struct sdw_master *mstr = to_sdw_master(dev);
+       int ret;
+
+       ret = dev_pm_domain_attach(dev, true);
+
+       if (ret != -EPROBE_DEFER) {
+               ret = sdrv->probe(mstr, sdw_match_mstr(sdrv->id_table, mstr));
+               if (ret < 0)
+                       dev_pm_domain_detach(dev, true);
+       }
+
+       return ret;
+}
+
+static int sdw_slv_probe(struct device *dev)
+{
+       const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver);
+       struct sdw_slave *sdwslv = to_sdw_slave(dev);
+       int ret;
+
+       ret = dev_pm_domain_attach(dev, true);
+
+       if (ret != -EPROBE_DEFER) {
+               ret = sdrv->probe(sdwslv, sdw_match_slv(sdrv->id_table,
+                                                       sdwslv));
+               if (ret < 0)
+                       dev_pm_domain_detach(dev, true);
+       }
+
+       return ret;
+}
+
+static int sdw_mstr_remove(struct device *dev)
+{
+       const struct sdw_master_driver *sdrv =
+                               to_sdw_master_driver(dev->driver);
+       int ret;
+
+       ret = sdrv->remove(to_sdw_master(dev));
+       dev_pm_domain_detach(dev, true);
+       return ret;
+
+}
+
+static int sdw_slv_remove(struct device *dev)
+{
+       const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver);
+       int ret;
+
+       ret = sdrv->remove(to_sdw_slave(dev));
+       dev_pm_domain_detach(dev, true);
+
+       return ret;
+}
+
+static void sdw_slv_shutdown(struct device *dev)
+{
+       const struct sdw_slave_driver *sdrv =
+                               to_sdw_slave_driver(dev->driver);
+
+       sdrv->shutdown(to_sdw_slave(dev));
+}
+
+static void sdw_mstr_shutdown(struct device *dev)
+{
+       const struct sdw_master_driver *sdrv =
+                               to_sdw_master_driver(dev->driver);
+
+       sdrv->shutdown(to_sdw_master(dev));
+}
+
+static int sdw_match(struct device *dev, struct device_driver *driver)
+{
+       struct sdw_slave *sdw_slv;
+       struct sdw_master *sdw_mstr;
+
+       sdw_slv = sdw_slv_verify(dev);
+       if (sdw_slv)
+               return sdw_slv_match(dev, driver);
+
+       sdw_mstr = sdw_mstr_verify(dev);
+       if (sdw_mstr)
+               return sdw_mstr_match(dev, driver);
+
+       /*
+        * Returning 0 to calling function means match not found, so calling
+        * function will not call probe
+        */
+       return 0;
+
+}
+
+static const struct dev_pm_ops soundwire_pm = {
+       .suspend = pm_generic_suspend,
+       .resume = pm_generic_resume,
+       SET_RUNTIME_PM_OPS(
+               pm_generic_runtime_suspend,
+               pm_generic_runtime_resume,
+               NULL)
+};
+
+static struct bus_type sdw_bus_type = {
+       .name           = "soundwire",
+       .match          = sdw_match,
+       .pm             = &soundwire_pm,
+};
+
+/**
+ * snd_sdw_master_register_driver: SoundWire Master driver registration with
+ *     bus. This API will register the Master driver with the SoundWire
+ *     bus. It is typically called from the driver's module-init function.
+ *
+ * @driver: Master Driver to be associated with Master interface.
+ * @owner: Module owner, generally THIS module.
+ */
+int snd_sdw_master_register_driver(struct sdw_master_driver *driver,
+                               struct module *owner)
+{
+       int ret;
+
+       if (!driver->probe)
+               return -EINVAL;
+
+       if (!driver->ops->xfer_msg || !driver->ops->reset_page_addr)
+               return -EINVAL;
+
+       if (!driver->port_ops->dpn_set_port_params ||
+               !driver->port_ops->dpn_set_port_transport_params ||
+               !driver->port_ops->dpn_port_enable_ch)
+               return -EINVAL;
+
+       driver->driver.probe = sdw_mstr_probe;
+
+       if (driver->remove)
+               driver->driver.remove = sdw_mstr_remove;
+       if (driver->shutdown)
+               driver->driver.shutdown = sdw_mstr_shutdown;
+
+       /* add the driver to the list of sdw drivers in the driver core */
+       driver->driver.owner = owner;
+       driver->driver.bus = &sdw_bus_type;
+
+       /*
+        * When registration returns, the driver core will have called
+        * probe() for all matching-but-unbound Slaves, devices which are
+        * not bind to any driver still.
+        */
+       ret = driver_register(&driver->driver);
+       if (ret)
+               return ret;
+
+       pr_debug("sdw-core: driver [%s] registered\n", driver->driver.name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_sdw_master_register_driver);
+
+/**
+ * snd_sdw_slave_driver_register: SoundWire Slave driver registration with
+ *     bus. This API will register the Slave driver with the SoundWire bus.
+ *     It is typically called from the driver's module-init function.
+ *
+ * @driver: Driver to be associated with Slave.
+ * @owner: Module owner, generally THIS module.
+ */
+int snd_sdw_slave_driver_register(struct sdw_slave_driver *driver,
+                               struct module *owner)
+{
+       int ret;
+
+       if (driver->probe)
+               driver->driver.probe = sdw_slv_probe;
+       if (driver->remove)
+               driver->driver.remove = sdw_slv_remove;
+       if (driver->shutdown)
+               driver->driver.shutdown = sdw_slv_shutdown;
+
+       /* Add the driver to the list of sdw drivers in the driver core */
+       driver->driver.owner = owner;
+       driver->driver.bus = &sdw_bus_type;
+
+       /*
+        * When registration returns, the driver core will have called
+        * probe() for all matching-but-unbound Slaves.
+        */
+       ret = driver_register(&driver->driver);
+       if (ret)
+               return ret;
+
+       pr_debug("sdw-core: driver [%s] registered\n", driver->driver.name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_sdw_slave_driver_register);
+
+static int sdw_copy_aud_mod_prop(struct sdw_port_aud_mode_prop *slv_prop,
+                               struct sdw_port_aud_mode_prop *prop)
+{
+       /*
+        * Currently goto is used in API to perform different
+        * operations. TODO: Avoid usage of goto statement
+        */
+       memcpy(slv_prop, prop, sizeof(*prop));
+
+       if (!prop->num_bus_freq_cfgs)
+               goto handle_sample_rate;
+
+       slv_prop->clk_freq_buf = kcalloc(prop->num_bus_freq_cfgs,
+                                       sizeof(unsigned int),
+                                       GFP_KERNEL);
+
+       if (!slv_prop->clk_freq_buf)
+               goto mem_error;
+
+       memcpy(slv_prop->clk_freq_buf, prop->clk_freq_buf,
+                               (prop->num_bus_freq_cfgs *
+                               sizeof(unsigned int)));
+
+handle_sample_rate:
+
+       if (!prop->num_sample_rate_cfgs)
+               return 0;
+
+       slv_prop->sample_rate_buf = kcalloc(prop->num_sample_rate_cfgs,
+                                       sizeof(unsigned int),
+                                       GFP_KERNEL);
+
+       if (!slv_prop->sample_rate_buf)
+               goto mem_error;
+
+       memcpy(slv_prop->sample_rate_buf, prop->sample_rate_buf,
+                               (prop->num_sample_rate_cfgs *
+                               sizeof(unsigned int)));
+
+       return 0;
+
+mem_error:
+       kfree(prop->clk_freq_buf);
+       kfree(slv_prop->sample_rate_buf);
+       return -ENOMEM;
+
+}
+
+static int sdw_update_dpn_caps(struct sdw_dpn_caps *slv_dpn_cap,
+                                       struct sdw_dpn_caps *dpn_cap)
+{
+       int j, ret = 0;
+       struct sdw_port_aud_mode_prop *slv_prop, *prop;
+
+       /*
+        * Currently goto is used in API to perform different
+        * operations. TODO: Avoid usage of goto statement
+        */
+
+       /*
+        * slv_prop and prop are using to make copy of mode properties.
+        * prop holds mode properties received which needs to be updated to
+        * slv_prop.
+        */
+
+       memcpy(slv_dpn_cap, dpn_cap, sizeof(*dpn_cap));
+
+       /*
+        * Copy bps (bits per sample) buffer as part of Slave capabilities
+        */
+       if (!dpn_cap->num_bps)
+               goto handle_ch_cnt;
+
+       slv_dpn_cap->bps_buf = kcalloc(dpn_cap->num_bps, sizeof(u8),
+                                                       GFP_KERNEL);
+
+       if (!slv_dpn_cap->bps_buf) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       memcpy(slv_dpn_cap->bps_buf, dpn_cap->bps_buf,
+                       (dpn_cap->num_bps * sizeof(u8)));
+
+handle_ch_cnt:
+       if (!dpn_cap->num_ch_cnt)
+               goto handle_audio_mode_prop;
+
+       slv_dpn_cap->ch_cnt_buf = kcalloc(dpn_cap->num_ch_cnt, sizeof(u8),
+                                                       GFP_KERNEL);
+       if (!dpn_cap->num_ch_cnt) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* Copy channel count buffer as part of Slave capabilities */
+       memcpy(slv_dpn_cap->ch_cnt_buf, dpn_cap->ch_cnt_buf,
+                       (dpn_cap->num_ch_cnt * sizeof(u8)));
+
+handle_audio_mode_prop:
+
+       slv_dpn_cap->mode_properties = kzalloc((sizeof(*slv_prop) *
+                               dpn_cap->num_audio_modes),
+                               GFP_KERNEL);
+
+       if (!slv_dpn_cap->mode_properties) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       for (j = 0; j < dpn_cap->num_audio_modes; j++) {
+
+               prop = &dpn_cap->mode_properties[j];
+               slv_prop = &slv_dpn_cap->mode_properties[j];
+
+               /* Copy audio properties as part of Slave capabilities */
+               ret = sdw_copy_aud_mod_prop(slv_prop, prop);
+               if (ret < 0)
+                       goto error;
+       }
+
+       return ret;
+
+error:
+       kfree(slv_dpn_cap->mode_properties);
+       kfree(slv_dpn_cap->ch_cnt_buf);
+       kfree(slv_dpn_cap->bps_buf);
+       return ret;
+
+}
+
+/* Free all the memory allocated for registering the capabilities */
+static void sdw_unregister_slv_caps(struct sdw_slave *sdw,
+               unsigned int num_port_direction)
+{
+       int i, j, k;
+       struct sdw_slave_caps *caps = &sdw->priv.caps;
+       struct sdw_dpn_caps *dpn_cap;
+       struct sdw_port_aud_mode_prop *mode_prop;
+       u8 ports;
+
+       for (i = 0; i < num_port_direction; i++) {
+
+               if (i == SDW_DATA_DIR_OUT)
+                       ports = caps->num_src_ports;
+               else
+                       ports = caps->num_sink_ports;
+               for (j = 0; j < ports; j++) {
+                       dpn_cap = &caps->dpn_caps[i][j];
+                       kfree(dpn_cap->bps_buf);
+                       kfree(dpn_cap->ch_cnt_buf);
+
+                       for (k = 0; k < dpn_cap->num_audio_modes; k++) {
+                               mode_prop = dpn_cap->mode_properties;
+                               kfree(mode_prop->clk_freq_buf);
+                               kfree(mode_prop->sample_rate_buf);
+                       }
+               }
+       }
+}
+
+static inline void sdw_copy_slv_caps(struct sdw_slave *sdw,
+                               struct sdw_slave_caps *caps)
+{
+       struct sdw_slave_caps *slv_caps;
+
+       slv_caps = &sdw->priv.caps;
+
+       memcpy(slv_caps, caps, sizeof(*slv_caps));
+}
+
+/**
+ * snd_sdw_slave_register_caps: Register Slave device capabilities to the
+ *     bus driver. Since bus driver handles bunch of Slave register
+ *     programming it should be aware of Slave device capabilities. Slave
+ *     device is attached to bus based on enumeration. Once Slave driver is
+ *     attached to device and probe of Slave driver is called on device and
+ *     driver binding, Slave driver should call this function to register
+ *     its capabilities to bus. This should be the very first function to
+ *     bus driver from Slave driver once Slave driver is registered and
+ *     probed.
+ *
+ * @slave: SoundWire Slave handle.
+ * @cap: Slave caps to be registered to bus driver.
+ */
+int snd_sdw_slave_register_caps(struct sdw_slave *slave,
+                                       struct sdw_slave_caps *cap)
+{
+       struct sdw_slave_caps *caps;
+       struct sdw_dpn_caps *slv_dpn_cap, *dpn_cap;
+       int i, j, ret;
+       u8 ports;
+
+       caps = &slave->priv.caps;
+
+       sdw_copy_slv_caps(slave, cap);
+
+       for (i = 0; i < SDW_MAX_PORT_DIRECTIONS; i++) {
+               if (i == SDW_DATA_DIR_OUT)
+                       ports = caps->num_src_ports;
+               else
+                       ports = caps->num_sink_ports;
+
+               caps->dpn_caps[i] = kzalloc((sizeof(*slv_dpn_cap) *
+                                               ports), GFP_KERNEL);
+
+               if (caps->dpn_caps[i] == NULL) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+       }
+
+       for (i = 0; i < SDW_MAX_PORT_DIRECTIONS; i++) {
+
+               if (i == SDW_DATA_DIR_OUT)
+                       ports = caps->num_src_ports;
+               else
+                       ports = caps->num_sink_ports;
+
+               for (j = 0; j < ports; j++) {
+
+                       dpn_cap = &cap->dpn_caps[i][j];
+                       slv_dpn_cap = &caps->dpn_caps[i][j];
+
+                       ret = sdw_update_dpn_caps(&caps->dpn_caps[i][j],
+                                               &cap->dpn_caps[i][j]);
+                       if (ret < 0) {
+                               dev_err(&slave->mstr->dev, "Failed to update 
Slave caps ret = %d\n", ret);
+                               goto error;
+                       }
+               }
+       }
+
+       slave->priv.slave_cap_updated = true;
+
+       return 0;
+
+error:
+       sdw_unregister_slv_caps(slave, i);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_sdw_slave_register_caps);
+
+/**
+ * snd_sdw_master_add: Registers the SoundWire Master interface. This needs
+ *     to be called for each Master interface supported by SoC. This
+ *     represents One clock and data line (Optionally multiple data lanes)
+ *     of Master interface.
+ *
+ * @master: the Master to be added.
+ */
+int snd_sdw_master_add(struct sdw_master *master)
+{
+       int i, id, ret;
+       struct sdw_bus *sdw_bus = NULL;
+
+       /* Sanity checks */
+       if (unlikely(master->name[0] == '\0')) {
+               pr_err("sdw-core: Attempt to register a master with no 
name!\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&snd_sdw_core.core_mutex);
+
+       /* Always start bus with 0th Index */
+       id = idr_alloc(&snd_sdw_core.idr, master, 0, 0, GFP_KERNEL);
+
+       if (id < 0) {
+               mutex_unlock(&snd_sdw_core.core_mutex);
+               return id;
+       }
+
+       master->nr = id;
+
+       /*
+        * Initialize the DeviceNumber in the Master structure. Each of
+        * these is assigned to the Slaves enumerating on this Master
+        * interface.
+        */
+       for (i = 0; i <= SDW_MAX_DEVICES; i++)
+               master->sdw_addr[i].dev_num = i;
+
+       mutex_init(&master->lock);
+       mutex_init(&master->msg_lock);
+       INIT_LIST_HEAD(&master->slv_list);
+       INIT_LIST_HEAD(&master->mstr_rt_list);
+
+       sdw_bus = kzalloc(sizeof(*sdw_bus), GFP_KERNEL);
+       if (!sdw_bus) {
+               ret = -ENOMEM;
+               goto alloc_failed;
+       }
+
+       sdw_bus->mstr = master;
+       master->bus = sdw_bus;
+
+       dev_set_name(&master->dev, "sdw-%d", master->nr);
+       master->dev.bus = &sdw_bus_type;
+       master->dev.type = &sdw_mstr_type;
+
+       ret = device_register(&master->dev);
+       if (ret < 0)
+               goto dev_reg_failed;
+
+       dev_dbg(&master->dev, "master [%s] registered\n", master->name);
+
+       /*
+        * Add bus to the list of buses inside core. This is list of Slave
+        * devices enumerated on this bus. Adding new devices at end. It can
+        * be added at any location in list.
+        */
+       list_add_tail(&sdw_bus->bus_node, &snd_sdw_core.bus_list);
+       mutex_unlock(&snd_sdw_core.core_mutex);
+
+       return 0;
+
+dev_reg_failed:
+       kfree(sdw_bus);
+alloc_failed:
+       idr_remove(&snd_sdw_core.idr, master->nr);
+       mutex_unlock(&snd_sdw_core.core_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_sdw_master_add);
+
+static void sdw_unregister_slv(struct sdw_slave *sdw_slv)
+{
+       struct sdw_master *mstr;
+
+       mstr = sdw_slave_to_master(sdw_slv);
+
+       sdw_unregister_slv_caps(sdw_slv, SDW_MAX_PORT_DIRECTIONS);
+
+       mutex_lock(&mstr->lock);
+       list_del(&sdw_slv->priv.node);
+       mutex_unlock(&mstr->lock);
+
+       mstr->sdw_addr[sdw_slv->dev_num].assigned = false;
+
+       device_unregister(&sdw_slv->dev);
+       kfree(sdw_slv);
+}
+
+static int __unregister_slv(struct device *dev, void *dummy)
+{
+       struct sdw_slave *slave = sdw_slv_verify(dev);
+
+       if (slave)
+               sdw_unregister_slv(slave);
+
+       return 0;
+}
+
+/**
+ * snd_sdw_master_del - unregister SDW Master
+ *
+ * @master: the Master being unregistered
+ */
+void snd_sdw_master_del(struct sdw_master *master)
+{
+       struct sdw_master *found;
+
+       /* First make sure that this Master was ever added */
+       mutex_lock(&snd_sdw_core.core_mutex);
+       found = idr_find(&snd_sdw_core.idr, master->nr);
+
+       if (found != master) {
+               pr_debug("sdw-core: attempting to delete unregistered master 
[%s]\n",
+                               master->name);
+               mutex_unlock(&snd_sdw_core.core_mutex);
+               return;
+       }
+       /*
+        * Detach any active Slaves. This can't fail, thus we do not check
+        * the returned value.
+        */
+       device_for_each_child(&master->dev, NULL, __unregister_slv);
+
+       /* device name is gone after device_unregister */
+       dev_dbg(&master->dev, "master [%s] unregistered\n", master->name);
+
+       /* wait until all references to the device are gone */
+       init_completion(&master->slv_released_complete);
+       device_unregister(&master->dev);
+       wait_for_completion(&master->slv_released_complete);
+
+       /* free bus id */
+       idr_remove(&snd_sdw_core.idr, master->nr);
+       mutex_unlock(&snd_sdw_core.core_mutex);
+
+       /*
+        * Clear the device structure in case this Master is ever going to
+        * be added again
+        */
+       memset(&master->dev, 0, sizeof(master->dev));
+}
+EXPORT_SYMBOL_GPL(snd_sdw_master_del);
+
+/**
+ * snd_sdw_master_get: Return the Master handle from Master number.
+ *     Increments the reference count of the module. Similar to
+ *     i2c_get_adapter.
+ *
+ * @nr: Master number.
+ *
+ * Returns Master handle on success, else NULL
+ */
+struct sdw_master *snd_sdw_master_get(int nr)
+{
+       struct sdw_master *master;
+
+       mutex_lock(&snd_sdw_core.core_mutex);
+
+       master = idr_find(&snd_sdw_core.idr, nr);
+       if (master && !try_module_get(master->driver->driver.owner))
+               master = NULL;
+
+       mutex_unlock(&snd_sdw_core.core_mutex);
+
+       return master;
+}
+EXPORT_SYMBOL_GPL(snd_sdw_master_get);
+
+/**
+ * snd_sdw_master_put: Reverses the effect of sdw_master_get
+ *
+ * @master: Master handle.
+ */
+void snd_sdw_master_put(struct sdw_master *master)
+{
+       if (master)
+               module_put(master->driver->driver.owner);
+}
+EXPORT_SYMBOL_GPL(snd_sdw_master_put);
+
+static void sdw_exit(void)
+{
+       bus_unregister(&sdw_bus_type);
+}
+
+static int sdw_init(void)
+{
+       int retval;
+
+       mutex_init(&snd_sdw_core.core_mutex);
+       INIT_LIST_HEAD(&snd_sdw_core.bus_list);
+       idr_init(&snd_sdw_core.idr);
+       retval = bus_register(&sdw_bus_type);
+
+       if (retval)
+               bus_unregister(&sdw_bus_type);
+       return retval;
+}
+
+subsys_initcall(sdw_init);
+module_exit(sdw_exit);
+
+MODULE_AUTHOR("Hardik Shah <[email protected]>");
+MODULE_AUTHOR("Sanyog Kale <[email protected]>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SoundWire bus driver");
+MODULE_ALIAS("platform:soundwire");
diff --git a/sound/sdw/sdw_priv.h b/sound/sdw/sdw_priv.h
new file mode 100644
index 0000000..5911aa6
--- /dev/null
+++ b/sound/sdw/sdw_priv.h
@@ -0,0 +1,102 @@
+/*
+ * sdw_priv.h - Private definition for SoundWire bus interface.
+ *
+ * Author: Hardik Shah <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef _LINUX_SDW_PRIV_H
+#define _LINUX_SDW_PRIV_H
+
+/**
+ * sdw_driver: Structure to typecast both Master and Slave driver to generic
+ *     SoundWire driver, to find out the driver type.
+ *
+ * @driver_type: Type of SoundWire driver, Master or Slave.
+ * @driver: Generic Linux driver.
+ */
+struct sdw_driver {
+       enum sdw_driver_type driver_type;
+       struct device_driver driver;
+};
+#define to_sdw_driver(d)                       \
+               container_of(d, struct sdw_driver, driver)
+/**
+ * sdw_bus: Bus structure holding bus related information.
+ *
+ * @bus_node: Node to add the bus in the sdw_core list.
+ * @mstr: Master reference for the bus.
+ */
+
+struct sdw_bus {
+       struct list_head bus_node;
+       struct sdw_master *mstr;
+};
+
+/**
+ * snd_sdw_core: Global SoundWire structure. It handles all the streams
+ *     spawned across masters and has list of bus structure per every
+ *     Master registered.
+ *
+ * @bus_list: List of all the bus instance.
+ * @core_mutex: Global lock for all bus instances.
+ * @idr: For identifying the registered buses.
+ */
+struct snd_sdw_core {
+       struct list_head bus_list;
+       struct mutex core_mutex;
+       struct idr idr;
+};
+
+#endif /* _LINUX_SDW_PRIV_H */
-- 
1.7.9.5

Reply via email to