Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933499AbcJUMme (ORCPT ); Fri, 21 Oct 2016 08:42:34 -0400 Received: from mga01.intel.com ([192.55.52.88]:65217 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933022AbcJUMkP (ORCPT ); Fri, 21 Oct 2016 08:40:15 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.31,376,1473145200"; d="scan'208";a="22071555" From: Hardik Shah To: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org Cc: tiwai@suse.de, pierre-louis.bossart@linux.intel.com, broonie@kernel.org, lgirdwood@gmail.com, plai@codeaurora.org, patches.audio@intel.com, Hardik Shah , Sanyog Kale Subject: [RFC 06/14] SoundWire: Add register/unregister APIs Date: Fri, 21 Oct 2016 18:11:04 +0530 Message-Id: <1477053673-16021-7-git-send-email-hardik.t.shah@intel.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1477053673-16021-1-git-send-email-hardik.t.shah@intel.com> References: <1477053673-16021-1-git-send-email-hardik.t.shah@intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 29404 Lines: 1068 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 Signed-off-by: Sanyog Kale Reviewed-by: Pierre-Louis Bossart --- 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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Sanyog Kale "); +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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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