From dac2787892da739d98c4c761ba08cf768ea05f3a Mon Sep 17 00:00:00 2001 From: spakkkk Date: Mon, 19 Oct 2020 16:09:41 +0000 Subject: [PATCH] drivers: soundwire: nuke it! --- drivers/Kconfig | 2 - drivers/Makefile | 1 - drivers/soundwire/Kconfig | 33 - drivers/soundwire/Makefile | 18 - drivers/soundwire/bus.c | 1042 ------------------- drivers/soundwire/bus.h | 143 --- drivers/soundwire/bus_type.c | 188 ---- drivers/soundwire/cadence_master.c | 1184 ---------------------- drivers/soundwire/cadence_master.h | 199 ---- drivers/soundwire/intel.c | 882 ---------------- drivers/soundwire/intel.h | 27 - drivers/soundwire/intel_init.c | 201 ---- drivers/soundwire/mipi_disco.c | 401 -------- drivers/soundwire/slave.c | 114 --- drivers/soundwire/stream.c | 1496 ---------------------------- 15 files changed, 5931 deletions(-) delete mode 100644 drivers/soundwire/Kconfig delete mode 100644 drivers/soundwire/Makefile delete mode 100644 drivers/soundwire/bus.c delete mode 100644 drivers/soundwire/bus.h delete mode 100644 drivers/soundwire/bus_type.c delete mode 100644 drivers/soundwire/cadence_master.c delete mode 100644 drivers/soundwire/cadence_master.h delete mode 100644 drivers/soundwire/intel.c delete mode 100644 drivers/soundwire/intel.h delete mode 100644 drivers/soundwire/intel_init.c delete mode 100644 drivers/soundwire/mipi_disco.c delete mode 100644 drivers/soundwire/slave.c delete mode 100644 drivers/soundwire/stream.c diff --git a/drivers/Kconfig b/drivers/Kconfig index 427228a2233c..ba67a47a1b32 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -159,8 +159,6 @@ source "drivers/remoteproc/Kconfig" source "drivers/rpmsg/Kconfig" -source "drivers/soundwire/Kconfig" - source "drivers/soc/Kconfig" source "drivers/devfreq/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 9a258f18ec6f..aa04cd1e9e5f 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -158,7 +158,6 @@ obj-$(CONFIG_MAILBOX) += mailbox/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-$(CONFIG_RPMSG) += rpmsg/ -obj-$(CONFIG_SOUNDWIRE) += soundwire/ obj-$(CONFIG_ENERGY_MODEL) += energy_model/ diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig deleted file mode 100644 index c7708feaa62e..000000000000 --- a/drivers/soundwire/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -# -# SoundWire subsystem configuration -# - -menuconfig SOUNDWIRE - tristate "SoundWire support" - depends on ACPI - help - SoundWire is a 2-Pin interface with data and clock line ratified - by the MIPI Alliance. SoundWire is used for transporting data - typically related to audio functions. SoundWire interface is - optimized to integrate audio devices in mobile or mobile inspired - systems. Say Y to enable this subsystem, N if you do not have such - a device - -if SOUNDWIRE - -comment "SoundWire Devices" - -config SOUNDWIRE_CADENCE - tristate - -config SOUNDWIRE_INTEL - tristate "Intel SoundWire Master driver" - select SOUNDWIRE_CADENCE - depends on X86 && ACPI && SND_SOC - ---help--- - SoundWire Intel Master driver. - If you have an Intel platform which has a SoundWire Master then - enable this config option to get the SoundWire support for that - device. - -endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile deleted file mode 100644 index 1e2c00163142..000000000000 --- a/drivers/soundwire/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Makefile for soundwire core -# - -#Bus Objs -soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o -obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o - -#Cadence Objs -soundwire-cadence-objs := cadence_master.o -obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o - -#Intel driver -soundwire-intel-objs := intel.o -obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o - -soundwire-intel-init-objs := intel_init.o -obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel-init.o diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c deleted file mode 100644 index 0089b606b70d..000000000000 --- a/drivers/soundwire/bus.c +++ /dev/null @@ -1,1042 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -#include -#include -#include -#include -#include -#include "bus.h" - -/** - * sdw_add_bus_master() - add a bus Master instance - * @bus: bus instance - * - * Initializes the bus instance, read properties and create child - * devices. - */ -int sdw_add_bus_master(struct sdw_bus *bus) -{ - struct sdw_master_prop *prop = NULL; - int ret; - - if (!bus->dev) { - pr_err("SoundWire bus has no device"); - return -ENODEV; - } - - if (!bus->ops) { - dev_err(bus->dev, "SoundWire Bus ops are not set"); - return -EINVAL; - } - - mutex_init(&bus->msg_lock); - mutex_init(&bus->bus_lock); - INIT_LIST_HEAD(&bus->slaves); - INIT_LIST_HEAD(&bus->m_rt_list); - - if (bus->ops->read_prop) { - ret = bus->ops->read_prop(bus); - if (ret < 0) { - dev_err(bus->dev, "Bus read properties failed:%d", ret); - return ret; - } - } - - /* - * Device numbers in SoundWire are 0 thru 15. Enumeration device - * number (0), Broadcast device number (15), Group numbers (12 and - * 13) and Master device number (14) are not used for assignment so - * mask these and other higher bits. - */ - - /* Set higher order bits */ - *bus->assigned = ~GENMASK(SDW_BROADCAST_DEV_NUM, SDW_ENUM_DEV_NUM); - - /* Set enumuration device number and broadcast device number */ - set_bit(SDW_ENUM_DEV_NUM, bus->assigned); - set_bit(SDW_BROADCAST_DEV_NUM, bus->assigned); - - /* Set group device numbers and master device number */ - set_bit(SDW_GROUP12_DEV_NUM, bus->assigned); - set_bit(SDW_GROUP13_DEV_NUM, bus->assigned); - set_bit(SDW_MASTER_DEV_NUM, bus->assigned); - - /* - * SDW is an enumerable bus, but devices can be powered off. So, - * they won't be able to report as present. - * - * Create Slave devices based on Slaves described in - * the respective firmware (ACPI/DT) - */ - if (IS_ENABLED(CONFIG_ACPI) && ACPI_HANDLE(bus->dev)) - ret = sdw_acpi_find_slaves(bus); - else - ret = -ENOTSUPP; /* No ACPI/DT so error out */ - - if (ret) { - dev_err(bus->dev, "Finding slaves failed:%d\n", ret); - return ret; - } - - /* - * Initialize clock values based on Master properties. The max - * frequency is read from max_freq property. Current assumption - * is that the bus will start at highest clock frequency when - * powered on. - * - * Default active bank will be 0 as out of reset the Slaves have - * to start with bank 0 (Table 40 of Spec) - */ - prop = &bus->prop; - bus->params.max_dr_freq = prop->max_freq * SDW_DOUBLE_RATE_FACTOR; - bus->params.curr_dr_freq = bus->params.max_dr_freq; - bus->params.curr_bank = SDW_BANK0; - bus->params.next_bank = SDW_BANK1; - - return 0; -} -EXPORT_SYMBOL(sdw_add_bus_master); - -static int sdw_delete_slave(struct device *dev, void *data) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_bus *bus = slave->bus; - - mutex_lock(&bus->bus_lock); - - if (slave->dev_num) /* clear dev_num if assigned */ - clear_bit(slave->dev_num, bus->assigned); - - list_del_init(&slave->node); - mutex_unlock(&bus->bus_lock); - - device_unregister(dev); - return 0; -} - -/** - * sdw_delete_bus_master() - 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) -{ - device_for_each_child(bus->dev, NULL, sdw_delete_slave); -} -EXPORT_SYMBOL(sdw_delete_bus_master); - -/* - * SDW IO Calls - */ - -static inline int find_response_code(enum sdw_command_response resp) -{ - switch (resp) { - case SDW_CMD_OK: - return 0; - - case SDW_CMD_IGNORED: - return -ENODATA; - - case SDW_CMD_TIMEOUT: - return -ETIMEDOUT; - - default: - return -EIO; - } -} - -static inline int do_transfer(struct sdw_bus *bus, struct sdw_msg *msg) -{ - int retry = bus->prop.err_threshold; - enum sdw_command_response resp; - int ret = 0, i; - - for (i = 0; i <= retry; i++) { - resp = bus->ops->xfer_msg(bus, msg); - ret = find_response_code(resp); - - /* if cmd is ok or ignored return */ - if (ret == 0 || ret == -ENODATA) - return ret; - } - - return ret; -} - -static inline int do_transfer_defer(struct sdw_bus *bus, - struct sdw_msg *msg, struct sdw_defer *defer) -{ - int retry = bus->prop.err_threshold; - enum sdw_command_response resp; - int ret = 0, i; - - defer->msg = msg; - defer->length = msg->len; - init_completion(&defer->complete); - - for (i = 0; i <= retry; i++) { - resp = bus->ops->xfer_msg_defer(bus, msg, defer); - ret = find_response_code(resp); - /* if cmd is ok or ignored return */ - if (ret == 0 || ret == -ENODATA) - return ret; - } - - return ret; -} - -static int sdw_reset_page(struct sdw_bus *bus, u16 dev_num) -{ - int retry = bus->prop.err_threshold; - enum sdw_command_response resp; - int ret = 0, i; - - for (i = 0; i <= retry; i++) { - resp = bus->ops->reset_page_addr(bus, dev_num); - ret = find_response_code(resp); - /* if cmd is ok or ignored return */ - if (ret == 0 || ret == -ENODATA) - return ret; - } - - return ret; -} - -/** - * sdw_transfer() - Synchronous transfer message to a SDW Slave device - * @bus: SDW bus - * @msg: SDW message to be xfered - */ -int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg) -{ - int ret; - - mutex_lock(&bus->msg_lock); - - ret = do_transfer(bus, msg); - if (ret != 0 && ret != -ENODATA) - dev_err(bus->dev, "trf on Slave %d failed:%d\n", - msg->dev_num, ret); - - if (msg->page) - sdw_reset_page(bus, msg->dev_num); - - mutex_unlock(&bus->msg_lock); - - return ret; -} - -/** - * sdw_transfer_defer() - Asynchronously transfer message to a SDW Slave device - * @bus: SDW bus - * @msg: SDW message to be xfered - * @defer: Defer block for signal completion - * - * Caller needs to hold the msg_lock lock while calling this - */ -int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, - struct sdw_defer *defer) -{ - int ret; - - if (!bus->ops->xfer_msg_defer) - return -ENOTSUPP; - - ret = do_transfer_defer(bus, msg, defer); - if (ret != 0 && ret != -ENODATA) - dev_err(bus->dev, "Defer trf on Slave %d failed:%d\n", - msg->dev_num, ret); - - if (msg->page) - sdw_reset_page(bus, msg->dev_num); - - return ret; -} - - -int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, - u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf) -{ - memset(msg, 0, sizeof(*msg)); - msg->addr = addr; /* addr is 16 bit and truncated here */ - msg->len = count; - msg->dev_num = dev_num; - msg->flags = flags; - msg->buf = buf; - msg->ssp_sync = false; - msg->page = false; - - if (addr < SDW_REG_NO_PAGE) { /* no paging area */ - return 0; - } else if (addr >= SDW_REG_MAX) { /* illegal addr */ - pr_err("SDW: Invalid address %x passed\n", addr); - return -EINVAL; - } - - if (addr < SDW_REG_OPTIONAL_PAGE) { /* 32k but no page */ - if (slave && !slave->prop.paging_support) - return 0; - /* no need for else as that will fall thru to paging */ - } - - /* paging mandatory */ - if (dev_num == SDW_ENUM_DEV_NUM || dev_num == SDW_BROADCAST_DEV_NUM) { - pr_err("SDW: Invalid device for paging :%d\n", dev_num); - return -EINVAL; - } - - if (!slave) { - pr_err("SDW: No slave for paging addr\n"); - return -EINVAL; - } else if (!slave->prop.paging_support) { - dev_err(&slave->dev, - "address %x needs paging but no support", addr); - return -EINVAL; - } - - msg->addr_page1 = (addr >> SDW_REG_SHIFT(SDW_SCP_ADDRPAGE1_MASK)); - msg->addr_page2 = (addr >> SDW_REG_SHIFT(SDW_SCP_ADDRPAGE2_MASK)); - msg->addr |= BIT(15); - msg->page = true; - - return 0; -} - -/** - * sdw_nread() - Read "n" contiguous SDW Slave registers - * @slave: SDW Slave - * @addr: Register address - * @count: length - * @val: Buffer for values to be read - */ -int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) -{ - struct sdw_msg msg; - int ret; - - ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_READ, val); - if (ret < 0) - return ret; - - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; - - ret = sdw_transfer(slave->bus, &msg); - pm_runtime_put(slave->bus->dev); - - return ret; -} -EXPORT_SYMBOL(sdw_nread); - -/** - * sdw_nwrite() - Write "n" contiguous SDW Slave registers - * @slave: SDW Slave - * @addr: Register address - * @count: length - * @val: Buffer for values to be read - */ -int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) -{ - struct sdw_msg msg; - int ret; - - ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_WRITE, val); - if (ret < 0) - return ret; - - ret = pm_runtime_get_sync(slave->bus->dev); - if (ret < 0) - return ret; - - ret = sdw_transfer(slave->bus, &msg); - pm_runtime_put(slave->bus->dev); - - return ret; -} -EXPORT_SYMBOL(sdw_nwrite); - -/** - * sdw_read() - Read a SDW Slave register - * @slave: SDW Slave - * @addr: Register address - */ -int sdw_read(struct sdw_slave *slave, u32 addr) -{ - u8 buf; - int ret; - - ret = sdw_nread(slave, addr, 1, &buf); - if (ret < 0) - return ret; - else - return buf; -} -EXPORT_SYMBOL(sdw_read); - -/** - * sdw_write() - Write a SDW Slave register - * @slave: SDW Slave - * @addr: Register address - * @value: Register value - */ -int sdw_write(struct sdw_slave *slave, u32 addr, u8 value) -{ - return sdw_nwrite(slave, addr, 1, &value); - -} -EXPORT_SYMBOL(sdw_write); - -/* - * SDW alert handling - */ - -/* called with bus_lock held */ -static struct sdw_slave *sdw_get_slave(struct sdw_bus *bus, int i) -{ - struct sdw_slave *slave = NULL; - - list_for_each_entry(slave, &bus->slaves, node) { - if (slave->dev_num == i) - return slave; - } - - return NULL; -} - -static int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id) -{ - - if ((slave->id.unique_id != id.unique_id) || - (slave->id.mfg_id != id.mfg_id) || - (slave->id.part_id != id.part_id) || - (slave->id.class_id != id.class_id)) - return -ENODEV; - - return 0; -} - -/* called with bus_lock held */ -static int sdw_get_device_num(struct sdw_slave *slave) -{ - int bit; - - bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES); - if (bit == SDW_MAX_DEVICES) { - bit = -ENODEV; - goto err; - } - - /* - * Do not update dev_num in Slave data structure here, - * Update once program dev_num is successful - */ - set_bit(bit, slave->bus->assigned); - -err: - return bit; -} - -static int sdw_assign_device_num(struct sdw_slave *slave) -{ - int ret, dev_num; - - /* check first if device number is assigned, if so reuse that */ - if (!slave->dev_num) { - mutex_lock(&slave->bus->bus_lock); - dev_num = sdw_get_device_num(slave); - mutex_unlock(&slave->bus->bus_lock); - if (dev_num < 0) { - dev_err(slave->bus->dev, "Get dev_num failed: %d", - dev_num); - return dev_num; - } - } else { - dev_info(slave->bus->dev, - "Slave already registered dev_num:%d", - slave->dev_num); - - /* Clear the slave->dev_num to transfer message on device 0 */ - dev_num = slave->dev_num; - slave->dev_num = 0; - - } - - ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num); - if (ret < 0) { - dev_err(&slave->dev, "Program device_num failed: %d", ret); - return ret; - } - - /* After xfer of msg, restore dev_num */ - slave->dev_num = dev_num; - - return 0; -} - -void sdw_extract_slave_id(struct sdw_bus *bus, - u64 addr, struct sdw_slave_id *id) -{ - dev_dbg(bus->dev, "SDW Slave Addr: %llx", addr); - - /* - * Spec definition - * Register Bit Contents - * DevId_0 [7:4] 47:44 sdw_version - * DevId_0 [3:0] 43:40 unique_id - * DevId_1 39:32 mfg_id [15:8] - * DevId_2 31:24 mfg_id [7:0] - * DevId_3 23:16 part_id [15:8] - * DevId_4 15:08 part_id [7:0] - * DevId_5 07:00 class_id - */ - id->sdw_version = (addr >> 44) & GENMASK(3, 0); - id->unique_id = (addr >> 40) & GENMASK(3, 0); - id->mfg_id = (addr >> 24) & GENMASK(15, 0); - id->part_id = (addr >> 8) & GENMASK(15, 0); - id->class_id = addr & GENMASK(7, 0); - - dev_dbg(bus->dev, - "SDW Slave class_id %x, part_id %x, mfg_id %x, unique_id %x, version %x", - id->class_id, id->part_id, id->mfg_id, - id->unique_id, id->sdw_version); - -} - -static int sdw_program_device_num(struct sdw_bus *bus) -{ - u8 buf[SDW_NUM_DEV_ID_REGISTERS] = {0}; - struct sdw_slave *slave, *_s; - struct sdw_slave_id id; - struct sdw_msg msg; - bool found; - int count = 0, ret; - u64 addr; - - /* No Slave, so use raw xfer api */ - ret = sdw_fill_msg(&msg, NULL, SDW_SCP_DEVID_0, - SDW_NUM_DEV_ID_REGISTERS, 0, SDW_MSG_FLAG_READ, buf); - if (ret < 0) - return ret; - - do { - ret = sdw_transfer(bus, &msg); - if (ret == -ENODATA) { /* end of device id reads */ - ret = 0; - break; - } - if (ret < 0) { - dev_err(bus->dev, "DEVID read fail:%d\n", ret); - break; - } - - /* - * Construct the addr and extract. Cast the higher shift - * bits to avoid truncation due to size limit. - */ - addr = buf[5] | (buf[4] << 8) | (buf[3] << 16) | - ((u64)buf[2] << 24) | ((u64)buf[1] << 32) | - ((u64)buf[0] << 40); - - sdw_extract_slave_id(bus, addr, &id); - - found = false; - /* Now compare with entries */ - list_for_each_entry_safe(slave, _s, &bus->slaves, node) { - if (sdw_compare_devid(slave, id) == 0) { - found = true; - - /* - * Assign a new dev_num to this Slave and - * not mark it present. It will be marked - * present after it reports ATTACHED on new - * dev_num - */ - ret = sdw_assign_device_num(slave); - if (ret) { - dev_err(slave->bus->dev, - "Assign dev_num failed:%d", - ret); - return ret; - } - - break; - } - } - - if (found == false) { - /* TODO: Park this device in Group 13 */ - dev_err(bus->dev, "Slave Entry not found"); - } - - count++; - - /* - * Check till error out or retry (count) exhausts. - * Device can drop off and rejoin during enumeration - * so count till twice the bound. - */ - - } while (ret == 0 && count < (SDW_MAX_DEVICES * 2)); - - return ret; -} - -static void sdw_modify_slave_status(struct sdw_slave *slave, - enum sdw_slave_status status) -{ - mutex_lock(&slave->bus->bus_lock); - slave->status = status; - mutex_unlock(&slave->bus->bus_lock); -} - -int sdw_configure_dpn_intr(struct sdw_slave *slave, - int port, bool enable, int mask) -{ - u32 addr; - int ret; - u8 val = 0; - - addr = SDW_DPN_INTMASK(port); - - /* Set/Clear port ready interrupt mask */ - if (enable) { - val |= mask; - val |= SDW_DPN_INT_PORT_READY; - } else { - val &= ~(mask); - val &= ~SDW_DPN_INT_PORT_READY; - } - - ret = sdw_update(slave, addr, (mask | SDW_DPN_INT_PORT_READY), val); - if (ret < 0) - dev_err(slave->bus->dev, - "SDW_DPN_INTMASK write failed:%d", val); - - return ret; -} - -static int sdw_initialize_slave(struct sdw_slave *slave) -{ - struct sdw_slave_prop *prop = &slave->prop; - int ret; - u8 val; - - /* - * Set bus clash, parity and SCP implementation - * defined interrupt mask - * TODO: Read implementation defined interrupt mask - * from Slave property - */ - val = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | - SDW_SCP_INT1_PARITY; - - /* Enable SCP interrupts */ - ret = sdw_update(slave, SDW_SCP_INTMASK1, val, val); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_SCP_INTMASK1 write failed:%d", ret); - return ret; - } - - /* No need to continue if DP0 is not present */ - if (!slave->prop.dp0_prop) - return 0; - - /* Enable DP0 interrupts */ - val = prop->dp0_prop->device_interrupts; - val |= SDW_DP0_INT_PORT_READY | SDW_DP0_INT_BRA_FAILURE; - - ret = sdw_update(slave, SDW_DP0_INTMASK, val, val); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_DP0_INTMASK read failed:%d", ret); - return val; - } - - return 0; -} - -static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status) -{ - u8 clear = 0, impl_int_mask; - int status, status2, ret, count = 0; - - status = sdw_read(slave, SDW_DP0_INT); - if (status < 0) { - dev_err(slave->bus->dev, - "SDW_DP0_INT read failed:%d", status); - return status; - } - - do { - - if (status & SDW_DP0_INT_TEST_FAIL) { - dev_err(&slave->dev, "Test fail for port 0"); - clear |= SDW_DP0_INT_TEST_FAIL; - } - - /* - * Assumption: PORT_READY interrupt will be received only for - * ports implementing Channel Prepare state machine (CP_SM) - */ - - if (status & SDW_DP0_INT_PORT_READY) { - complete(&slave->port_ready[0]); - clear |= SDW_DP0_INT_PORT_READY; - } - - if (status & SDW_DP0_INT_BRA_FAILURE) { - dev_err(&slave->dev, "BRA failed"); - clear |= SDW_DP0_INT_BRA_FAILURE; - } - - impl_int_mask = SDW_DP0_INT_IMPDEF1 | - SDW_DP0_INT_IMPDEF2 | SDW_DP0_INT_IMPDEF3; - - if (status & impl_int_mask) { - clear |= impl_int_mask; - *slave_status = clear; - } - - /* clear the interrupt */ - ret = sdw_write(slave, SDW_DP0_INT, clear); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_DP0_INT write failed:%d", ret); - return ret; - } - - /* Read DP0 interrupt again */ - status2 = sdw_read(slave, SDW_DP0_INT); - if (status2 < 0) { - dev_err(slave->bus->dev, - "SDW_DP0_INT read failed:%d", status2); - return status2; - } - status &= status2; - - count++; - - /* we can get alerts while processing so keep retrying */ - } while (status != 0 && count < SDW_READ_INTR_CLEAR_RETRY); - - if (count == SDW_READ_INTR_CLEAR_RETRY) - dev_warn(slave->bus->dev, "Reached MAX_RETRY on DP0 read"); - - return ret; -} - -static int sdw_handle_port_interrupt(struct sdw_slave *slave, - int port, u8 *slave_status) -{ - u8 clear = 0, impl_int_mask; - int status, status2, ret, count = 0; - u32 addr; - - if (port == 0) - return sdw_handle_dp0_interrupt(slave, slave_status); - - addr = SDW_DPN_INT(port); - status = sdw_read(slave, addr); - if (status < 0) { - dev_err(slave->bus->dev, - "SDW_DPN_INT read failed:%d", status); - - return status; - } - - do { - - if (status & SDW_DPN_INT_TEST_FAIL) { - dev_err(&slave->dev, "Test fail for port:%d", port); - clear |= SDW_DPN_INT_TEST_FAIL; - } - - /* - * Assumption: PORT_READY interrupt will be received only - * for ports implementing CP_SM. - */ - if (status & SDW_DPN_INT_PORT_READY) { - complete(&slave->port_ready[port]); - clear |= SDW_DPN_INT_PORT_READY; - } - - impl_int_mask = SDW_DPN_INT_IMPDEF1 | - SDW_DPN_INT_IMPDEF2 | SDW_DPN_INT_IMPDEF3; - - - if (status & impl_int_mask) { - clear |= impl_int_mask; - *slave_status = clear; - } - - /* clear the interrupt */ - ret = sdw_write(slave, addr, clear); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_DPN_INT write failed:%d", ret); - return ret; - } - - /* Read DPN interrupt again */ - status2 = sdw_read(slave, addr); - if (status2 < 0) { - dev_err(slave->bus->dev, - "SDW_DPN_INT read failed:%d", status2); - return status2; - } - status &= status2; - - count++; - - /* we can get alerts while processing so keep retrying */ - } while (status != 0 && count < SDW_READ_INTR_CLEAR_RETRY); - - if (count == SDW_READ_INTR_CLEAR_RETRY) - dev_warn(slave->bus->dev, "Reached MAX_RETRY on port read"); - - return ret; -} - -static int sdw_handle_slave_alerts(struct sdw_slave *slave) -{ - struct sdw_slave_intr_status slave_intr; - u8 clear = 0, bit, port_status[15] = {0}; - int port_num, stat, ret, count = 0; - unsigned long port; - bool slave_notify = false; - u8 buf, buf2[2], _buf, _buf2[2]; - - sdw_modify_slave_status(slave, SDW_SLAVE_ALERT); - - /* Read Instat 1, Instat 2 and Instat 3 registers */ - buf = ret = sdw_read(slave, SDW_SCP_INT1); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_SCP_INT1 read failed:%d", ret); - return ret; - } - - ret = sdw_nread(slave, SDW_SCP_INTSTAT2, 2, buf2); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_SCP_INT2/3 read failed:%d", ret); - return ret; - } - - do { - /* - * Check parity, bus clash and Slave (impl defined) - * interrupt - */ - if (buf & SDW_SCP_INT1_PARITY) { - dev_err(&slave->dev, "Parity error detected"); - clear |= SDW_SCP_INT1_PARITY; - } - - if (buf & SDW_SCP_INT1_BUS_CLASH) { - dev_err(&slave->dev, "Bus clash error detected"); - clear |= SDW_SCP_INT1_BUS_CLASH; - } - - /* - * When bus clash or parity errors are detected, such errors - * are unlikely to be recoverable errors. - * TODO: In such scenario, reset bus. Make this configurable - * via sysfs property with bus reset being the default. - */ - - if (buf & SDW_SCP_INT1_IMPL_DEF) { - dev_dbg(&slave->dev, "Slave impl defined interrupt\n"); - clear |= SDW_SCP_INT1_IMPL_DEF; - slave_notify = true; - } - - /* Check port 0 - 3 interrupts */ - port = buf & SDW_SCP_INT1_PORT0_3; - - /* To get port number corresponding to bits, shift it */ - port = port >> SDW_REG_SHIFT(SDW_SCP_INT1_PORT0_3); - for_each_set_bit(bit, &port, 8) { - sdw_handle_port_interrupt(slave, bit, - &port_status[bit]); - - } - - /* Check if cascade 2 interrupt is present */ - if (buf & SDW_SCP_INT1_SCP2_CASCADE) { - port = buf2[0] & SDW_SCP_INTSTAT2_PORT4_10; - for_each_set_bit(bit, &port, 8) { - /* scp2 ports start from 4 */ - port_num = bit + 3; - sdw_handle_port_interrupt(slave, - port_num, - &port_status[port_num]); - } - } - - /* now check last cascade */ - if (buf2[0] & SDW_SCP_INTSTAT2_SCP3_CASCADE) { - port = buf2[1] & SDW_SCP_INTSTAT3_PORT11_14; - for_each_set_bit(bit, &port, 8) { - /* scp3 ports start from 11 */ - port_num = bit + 10; - sdw_handle_port_interrupt(slave, - port_num, - &port_status[port_num]); - } - } - - /* Update the Slave driver */ - if (slave_notify && (slave->ops) && - (slave->ops->interrupt_callback)) { - slave_intr.control_port = clear; - memcpy(slave_intr.port, &port_status, - sizeof(slave_intr.port)); - - slave->ops->interrupt_callback(slave, &slave_intr); - } - - /* Ack interrupt */ - ret = sdw_write(slave, SDW_SCP_INT1, clear); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_SCP_INT1 write failed:%d", ret); - return ret; - } - - /* - * Read status again to ensure no new interrupts arrived - * while servicing interrupts. - */ - _buf = ret = sdw_read(slave, SDW_SCP_INT1); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_SCP_INT1 read failed:%d", ret); - return ret; - } - - ret = sdw_nread(slave, SDW_SCP_INTSTAT2, 2, _buf2); - if (ret < 0) { - dev_err(slave->bus->dev, - "SDW_SCP_INT2/3 read failed:%d", ret); - return ret; - } - - /* Make sure no interrupts are pending */ - buf &= _buf; - buf2[0] &= _buf2[0]; - buf2[1] &= _buf2[1]; - stat = buf || buf2[0] || buf2[1]; - - /* - * Exit loop if Slave is continuously in ALERT state even - * after servicing the interrupt multiple times. - */ - count++; - - /* we can get alerts while processing so keep retrying */ - } while (stat != 0 && count < SDW_READ_INTR_CLEAR_RETRY); - - if (count == SDW_READ_INTR_CLEAR_RETRY) - dev_warn(slave->bus->dev, "Reached MAX_RETRY on alert read"); - - return ret; -} - -static int sdw_update_slave_status(struct sdw_slave *slave, - enum sdw_slave_status status) -{ - if ((slave->ops) && (slave->ops->update_status)) - return slave->ops->update_status(slave, status); - - return 0; -} - -/** - * sdw_handle_slave_status() - Handle Slave status - * @bus: SDW bus instance - * @status: Status for all Slave(s) - */ -int sdw_handle_slave_status(struct sdw_bus *bus, - enum sdw_slave_status status[]) -{ - enum sdw_slave_status prev_status; - struct sdw_slave *slave; - int i, ret = 0; - - if (status[0] == SDW_SLAVE_ATTACHED) { - ret = sdw_program_device_num(bus); - if (ret) - dev_err(bus->dev, "Slave attach failed: %d", ret); - } - - /* Continue to check other slave statuses */ - for (i = 1; i <= SDW_MAX_DEVICES; i++) { - mutex_lock(&bus->bus_lock); - if (test_bit(i, bus->assigned) == false) { - mutex_unlock(&bus->bus_lock); - continue; - } - mutex_unlock(&bus->bus_lock); - - slave = sdw_get_slave(bus, i); - if (!slave) - continue; - - switch (status[i]) { - case SDW_SLAVE_UNATTACHED: - if (slave->status == SDW_SLAVE_UNATTACHED) - break; - - sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); - break; - - case SDW_SLAVE_ALERT: - ret = sdw_handle_slave_alerts(slave); - if (ret) - dev_err(bus->dev, - "Slave %d alert handling failed: %d", - i, ret); - break; - - case SDW_SLAVE_ATTACHED: - if (slave->status == SDW_SLAVE_ATTACHED) - break; - - prev_status = slave->status; - sdw_modify_slave_status(slave, SDW_SLAVE_ATTACHED); - - if (prev_status == SDW_SLAVE_ALERT) - break; - - ret = sdw_initialize_slave(slave); - if (ret) - dev_err(bus->dev, - "Slave %d initialization failed: %d", - i, ret); - - break; - - default: - dev_err(bus->dev, "Invalid slave %d status:%d", - i, status[i]); - break; - } - - ret = sdw_update_slave_status(slave, status[i]); - if (ret) - dev_err(slave->bus->dev, - "Update Slave status failed:%d", ret); - - } - - return ret; -} -EXPORT_SYMBOL(sdw_handle_slave_status); diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h deleted file mode 100644 index 3b15c4e25a3a..000000000000 --- a/drivers/soundwire/bus.h +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -#ifndef __SDW_BUS_H -#define __SDW_BUS_H - -#if IS_ENABLED(CONFIG_ACPI) -int sdw_acpi_find_slaves(struct sdw_bus *bus); -#else -static inline int sdw_acpi_find_slaves(struct sdw_bus *bus) -{ - return -ENOTSUPP; -} -#endif - -void sdw_extract_slave_id(struct sdw_bus *bus, - u64 addr, struct sdw_slave_id *id); - -enum { - SDW_MSG_FLAG_READ = 0, - SDW_MSG_FLAG_WRITE, -}; - -/** - * struct sdw_msg - Message structure - * @addr: Register address accessed in the Slave - * @len: number of messages - * @dev_num: Slave device number - * @addr_page1: SCP address page 1 Slave register - * @addr_page2: SCP address page 2 Slave register - * @flags: transfer flags, indicate if xfer is read or write - * @buf: message data buffer - * @ssp_sync: Send message at SSP (Stream Synchronization Point) - * @page: address requires paging - */ -struct sdw_msg { - u16 addr; - u16 len; - u8 dev_num; - u8 addr_page1; - u8 addr_page2; - u8 flags; - u8 *buf; - bool ssp_sync; - bool page; -}; - -#define SDW_DOUBLE_RATE_FACTOR 2 - -extern int rows[SDW_FRAME_ROWS]; -extern int cols[SDW_FRAME_COLS]; - -/** - * sdw_port_runtime: Runtime port parameters for Master or Slave - * - * @num: Port number. For audio streams, valid port number ranges from - * [1,14] - * @ch_mask: Channel mask - * @transport_params: Transport parameters - * @port_params: Port parameters - * @port_node: List node for Master or Slave port_list - * - * SoundWire spec has no mention of ports for Master interface but the - * concept is logically extended. - */ -struct sdw_port_runtime { - int num; - int ch_mask; - struct sdw_transport_params transport_params; - struct sdw_port_params port_params; - struct list_head port_node; -}; - -/** - * sdw_slave_runtime: Runtime Stream parameters for Slave - * - * @slave: Slave handle - * @direction: Data direction for Slave - * @ch_count: Number of channels handled by the Slave for - * this stream - * @m_rt_node: sdw_master_runtime list node - * @port_list: List of Slave Ports configured for this stream - */ -struct sdw_slave_runtime { - struct sdw_slave *slave; - enum sdw_data_direction direction; - unsigned int ch_count; - struct list_head m_rt_node; - struct list_head port_list; -}; - -/** - * sdw_master_runtime: Runtime stream parameters for Master - * - * @bus: Bus handle - * @stream: Stream runtime handle - * @direction: Data direction for Master - * @ch_count: Number of channels handled by the Master for - * this stream, can be zero. - * @slave_rt_list: Slave runtime list - * @port_list: List of Master Ports configured for this stream, can be zero. - * @bus_node: sdw_bus m_rt_list node - */ -struct sdw_master_runtime { - struct sdw_bus *bus; - struct sdw_stream_runtime *stream; - enum sdw_data_direction direction; - unsigned int ch_count; - struct list_head slave_rt_list; - struct list_head port_list; - struct list_head bus_node; -}; - -struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, - enum sdw_data_direction direction, - unsigned int port_num); -int sdw_configure_dpn_intr(struct sdw_slave *slave, int port, - bool enable, int mask); - -int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg); -int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, - struct sdw_defer *defer); - -#define SDW_READ_INTR_CLEAR_RETRY 10 - -int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave, - u32 addr, size_t count, u16 dev_num, u8 flags, u8 *buf); - -/* Read-Modify-Write Slave register */ -static inline int -sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val) -{ - int tmp; - - tmp = sdw_read(slave, addr); - if (tmp < 0) - return tmp; - - tmp = (tmp & ~mask) | val; - return sdw_write(slave, addr, tmp); -} - -#endif /* __SDW_BUS_H */ diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c deleted file mode 100644 index 414621f3c43c..000000000000 --- a/drivers/soundwire/bus_type.c +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright(c) 2015-17 Intel Corporation. - -#include -#include -#include -#include -#include - -/** - * sdw_get_device_id - find the matching SoundWire device id - * @slave: SoundWire Slave Device - * @drv: SoundWire Slave Driver - * - * The match is done by comparing the mfg_id and part_id from the - * struct sdw_device_id. - */ -static const struct sdw_device_id * -sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) -{ - const struct sdw_device_id *id = drv->id_table; - - while (id && id->mfg_id) { - if (slave->id.mfg_id == id->mfg_id && - slave->id.part_id == id->part_id) - return id; - id++; - } - - return NULL; -} - -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); - - return !!sdw_get_device_id(slave, drv); -} - -int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) -{ - /* modalias is sdw:mp */ - - return snprintf(buf, size, "sdw:m%04Xp%04X\n", - slave->id.mfg_id, slave->id.part_id); -} - -static int sdw_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - char modalias[32]; - - sdw_slave_modalias(slave, modalias, sizeof(modalias)); - - if (add_uevent_var(env, "MODALIAS=%s", modalias)) - return -ENOMEM; - - return 0; -} - -struct bus_type sdw_bus_type = { - .name = "soundwire", - .match = sdw_bus_match, - .uevent = sdw_uevent, -}; -EXPORT_SYMBOL_GPL(sdw_bus_type); - -static int sdw_drv_probe(struct device *dev) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); - const struct sdw_device_id *id; - int ret; - - id = sdw_get_device_id(slave, drv); - if (!id) - return -ENODEV; - - slave->ops = drv->ops; - - /* - * attach to power domain but don't turn on (last arg) - */ - ret = dev_pm_domain_attach(dev, false); - if (ret) - return ret; - - ret = drv->probe(slave, id); - if (ret) { - dev_err(dev, "Probe of %s failed: %d\n", drv->name, ret); - dev_pm_domain_detach(dev, false); - return ret; - } - - /* device is probed so let's read the properties now */ - if (slave->ops && slave->ops->read_prop) - slave->ops->read_prop(slave); - - /* - * Check for valid clk_stop_timeout, use DisCo worst case value of - * 300ms - * - * TODO: check the timeouts and driver removal case - */ - if (slave->prop.clk_stop_timeout == 0) - slave->prop.clk_stop_timeout = 300; - - slave->bus->clk_stop_timeout = max_t(u32, slave->bus->clk_stop_timeout, - slave->prop.clk_stop_timeout); - - return 0; -} - -static int sdw_drv_remove(struct device *dev) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); - int ret = 0; - - if (drv->remove) - ret = drv->remove(slave); - - dev_pm_domain_detach(dev, false); - - return ret; -} - -static void sdw_drv_shutdown(struct device *dev) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); - - if (drv->shutdown) - drv->shutdown(slave); -} - -/** - * __sdw_register_driver() - register a SoundWire Slave driver - * @drv: driver to register - * @owner: owning module/driver - * - * Return: zero on success, else a negative error code. - */ -int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) -{ - drv->driver.bus = &sdw_bus_type; - - if (!drv->probe) { - pr_err("driver %s didn't provide SDW probe routine\n", - drv->name); - return -EINVAL; - } - - drv->driver.owner = owner; - drv->driver.probe = sdw_drv_probe; - drv->driver.remove = sdw_drv_remove; - drv->driver.shutdown = sdw_drv_shutdown; - - return driver_register(&drv->driver); -} -EXPORT_SYMBOL_GPL(__sdw_register_driver); - -/** - * sdw_unregister_driver() - unregisters the SoundWire Slave driver - * @drv: driver to unregister - */ -void sdw_unregister_driver(struct sdw_driver *drv) -{ - driver_unregister(&drv->driver); -} -EXPORT_SYMBOL_GPL(sdw_unregister_driver); - -static int __init sdw_bus_init(void) -{ - return bus_register(&sdw_bus_type); -} - -static void __exit sdw_bus_exit(void) -{ - bus_unregister(&sdw_bus_type); -} - -postcore_initcall(sdw_bus_init); -module_exit(sdw_bus_exit); - -MODULE_DESCRIPTION("SoundWire bus"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c deleted file mode 100644 index 70f78eda037e..000000000000 --- a/drivers/soundwire/cadence_master.c +++ /dev/null @@ -1,1184 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -/* - * Cadence SoundWire Master module - * Used by Master driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "bus.h" -#include "cadence_master.h" - -#define CDNS_MCP_CONFIG 0x0 - -#define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24) -#define CDNS_MCP_CONFIG_MPREQ_DELAY GENMASK(20, 16) -#define CDNS_MCP_CONFIG_MMASTER BIT(7) -#define CDNS_MCP_CONFIG_BUS_REL BIT(6) -#define CDNS_MCP_CONFIG_SNIFFER BIT(5) -#define CDNS_MCP_CONFIG_SSPMOD BIT(4) -#define CDNS_MCP_CONFIG_CMD BIT(3) -#define CDNS_MCP_CONFIG_OP GENMASK(2, 0) -#define CDNS_MCP_CONFIG_OP_NORMAL 0 - -#define CDNS_MCP_CONTROL 0x4 - -#define CDNS_MCP_CONTROL_RST_DELAY GENMASK(10, 8) -#define CDNS_MCP_CONTROL_CMD_RST BIT(7) -#define CDNS_MCP_CONTROL_SOFT_RST BIT(6) -#define CDNS_MCP_CONTROL_SW_RST BIT(5) -#define CDNS_MCP_CONTROL_HW_RST BIT(4) -#define CDNS_MCP_CONTROL_CLK_PAUSE BIT(3) -#define CDNS_MCP_CONTROL_CLK_STOP_CLR BIT(2) -#define CDNS_MCP_CONTROL_CMD_ACCEPT BIT(1) -#define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0) - - -#define CDNS_MCP_CMDCTRL 0x8 -#define CDNS_MCP_SSPSTAT 0xC -#define CDNS_MCP_FRAME_SHAPE 0x10 -#define CDNS_MCP_FRAME_SHAPE_INIT 0x14 - -#define CDNS_MCP_CONFIG_UPDATE 0x18 -#define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0) - -#define CDNS_MCP_PHYCTRL 0x1C -#define CDNS_MCP_SSP_CTRL0 0x20 -#define CDNS_MCP_SSP_CTRL1 0x28 -#define CDNS_MCP_CLK_CTRL0 0x30 -#define CDNS_MCP_CLK_CTRL1 0x38 - -#define CDNS_MCP_STAT 0x40 - -#define CDNS_MCP_STAT_ACTIVE_BANK BIT(20) -#define CDNS_MCP_STAT_CLK_STOP BIT(16) - -#define CDNS_MCP_INTSTAT 0x44 -#define CDNS_MCP_INTMASK 0x48 - -#define CDNS_MCP_INT_IRQ BIT(31) -#define CDNS_MCP_INT_WAKEUP BIT(16) -#define CDNS_MCP_INT_SLAVE_RSVD BIT(15) -#define CDNS_MCP_INT_SLAVE_ALERT BIT(14) -#define CDNS_MCP_INT_SLAVE_ATTACH BIT(13) -#define CDNS_MCP_INT_SLAVE_NATTACH BIT(12) -#define CDNS_MCP_INT_SLAVE_MASK GENMASK(15, 12) -#define CDNS_MCP_INT_DPINT BIT(11) -#define CDNS_MCP_INT_CTRL_CLASH BIT(10) -#define CDNS_MCP_INT_DATA_CLASH BIT(9) -#define CDNS_MCP_INT_CMD_ERR BIT(7) -#define CDNS_MCP_INT_RX_WL BIT(2) -#define CDNS_MCP_INT_TXE BIT(1) - -#define CDNS_MCP_INTSET 0x4C - -#define CDNS_MCP_SLAVE_STAT 0x50 -#define CDNS_MCP_SLAVE_STAT_MASK GENMASK(1, 0) - -#define CDNS_MCP_SLAVE_INTSTAT0 0x54 -#define CDNS_MCP_SLAVE_INTSTAT1 0x58 -#define CDNS_MCP_SLAVE_INTSTAT_NPRESENT BIT(0) -#define CDNS_MCP_SLAVE_INTSTAT_ATTACHED BIT(1) -#define CDNS_MCP_SLAVE_INTSTAT_ALERT BIT(2) -#define CDNS_MCP_SLAVE_INTSTAT_RESERVED BIT(3) -#define CDNS_MCP_SLAVE_STATUS_BITS GENMASK(3, 0) -#define CDNS_MCP_SLAVE_STATUS_NUM 4 - -#define CDNS_MCP_SLAVE_INTMASK0 0x5C -#define CDNS_MCP_SLAVE_INTMASK1 0x60 - -#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(31, 0) -#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(15, 0) - -#define CDNS_MCP_PORT_INTSTAT 0x64 -#define CDNS_MCP_PDI_STAT 0x6C - -#define CDNS_MCP_FIFOLEVEL 0x78 -#define CDNS_MCP_FIFOSTAT 0x7C -#define CDNS_MCP_RX_FIFO_AVAIL GENMASK(5, 0) - -#define CDNS_MCP_CMD_BASE 0x80 -#define CDNS_MCP_RESP_BASE 0x80 -#define CDNS_MCP_CMD_LEN 0x20 -#define CDNS_MCP_CMD_WORD_LEN 0x4 - -#define CDNS_MCP_CMD_SSP_TAG BIT(31) -#define CDNS_MCP_CMD_COMMAND GENMASK(30, 28) -#define CDNS_MCP_CMD_DEV_ADDR GENMASK(27, 24) -#define CDNS_MCP_CMD_REG_ADDR_H GENMASK(23, 16) -#define CDNS_MCP_CMD_REG_ADDR_L GENMASK(15, 8) -#define CDNS_MCP_CMD_REG_DATA GENMASK(7, 0) - -#define CDNS_MCP_CMD_READ 2 -#define CDNS_MCP_CMD_WRITE 3 - -#define CDNS_MCP_RESP_RDATA GENMASK(15, 8) -#define CDNS_MCP_RESP_ACK BIT(0) -#define CDNS_MCP_RESP_NACK BIT(1) - -#define CDNS_DP_SIZE 128 - -#define CDNS_DPN_B0_CONFIG(n) (0x100 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B0_CH_EN(n) (0x104 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B0_SAMPLE_CTRL(n) (0x108 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B0_OFFSET_CTRL(n) (0x10C + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B0_HCTRL(n) (0x110 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B0_ASYNC_CTRL(n) (0x114 + CDNS_DP_SIZE * (n)) - -#define CDNS_DPN_B1_CONFIG(n) (0x118 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B1_CH_EN(n) (0x11C + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B1_SAMPLE_CTRL(n) (0x120 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B1_OFFSET_CTRL(n) (0x124 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B1_HCTRL(n) (0x128 + CDNS_DP_SIZE * (n)) -#define CDNS_DPN_B1_ASYNC_CTRL(n) (0x12C + CDNS_DP_SIZE * (n)) - -#define CDNS_DPN_CONFIG_BPM BIT(18) -#define CDNS_DPN_CONFIG_BGC GENMASK(17, 16) -#define CDNS_DPN_CONFIG_WL GENMASK(12, 8) -#define CDNS_DPN_CONFIG_PORT_DAT GENMASK(3, 2) -#define CDNS_DPN_CONFIG_PORT_FLOW GENMASK(1, 0) - -#define CDNS_DPN_SAMPLE_CTRL_SI GENMASK(15, 0) - -#define CDNS_DPN_OFFSET_CTRL_1 GENMASK(7, 0) -#define CDNS_DPN_OFFSET_CTRL_2 GENMASK(15, 8) - -#define CDNS_DPN_HCTRL_HSTOP GENMASK(3, 0) -#define CDNS_DPN_HCTRL_HSTART GENMASK(7, 4) -#define CDNS_DPN_HCTRL_LCTRL GENMASK(10, 8) - -#define CDNS_PORTCTRL 0x130 -#define CDNS_PORTCTRL_DIRN BIT(7) -#define CDNS_PORTCTRL_BANK_INVERT BIT(8) - -#define CDNS_PORT_OFFSET 0x80 - -#define CDNS_PDI_CONFIG(n) (0x1100 + (n) * 16) - -#define CDNS_PDI_CONFIG_SOFT_RESET BIT(24) -#define CDNS_PDI_CONFIG_CHANNEL GENMASK(15, 8) -#define CDNS_PDI_CONFIG_PORT GENMASK(4, 0) - -/* Driver defaults */ - -#define CDNS_DEFAULT_CLK_DIVIDER 0 -#define CDNS_DEFAULT_FRAME_SHAPE 0x30 -#define CDNS_DEFAULT_SSP_INTERVAL 0x18 -#define CDNS_TX_TIMEOUT 2000 - -#define CDNS_PCM_PDI_OFFSET 0x2 -#define CDNS_PDM_PDI_OFFSET 0x6 - -#define CDNS_SCP_RX_FIFOLEVEL 0x2 - -/* - * register accessor helpers - */ -static inline u32 cdns_readl(struct sdw_cdns *cdns, int offset) -{ - return readl(cdns->registers + offset); -} - -static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value) -{ - writel(value, cdns->registers + offset); -} - -static inline void cdns_updatel(struct sdw_cdns *cdns, - int offset, u32 mask, u32 val) -{ - u32 tmp; - - tmp = cdns_readl(cdns, offset); - tmp = (tmp & ~mask) | val; - cdns_writel(cdns, offset, tmp); -} - -static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) -{ - int timeout = 10; - u32 reg_read; - - writel(value, cdns->registers + offset); - - /* Wait for bit to be self cleared */ - do { - reg_read = readl(cdns->registers + offset); - if ((reg_read & value) == 0) - return 0; - - timeout--; - udelay(50); - } while (timeout != 0); - - return -EAGAIN; -} - -/* - * IO Calls - */ -static enum sdw_command_response cdns_fill_msg_resp( - struct sdw_cdns *cdns, - struct sdw_msg *msg, int count, int offset) -{ - int nack = 0, no_ack = 0; - int i; - - /* check message response */ - for (i = 0; i < count; i++) { - if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { - no_ack = 1; - dev_dbg(cdns->dev, "Msg Ack not received\n"); - if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { - nack = 1; - dev_err(cdns->dev, "Msg NACK received\n"); - } - } - } - - if (nack) { - dev_err(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); - return SDW_CMD_FAIL; - } else if (no_ack) { - dev_dbg(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); - return SDW_CMD_IGNORED; - } - - /* fill response */ - for (i = 0; i < count; i++) - msg->buf[i + offset] = cdns->response_buf[i] >> - SDW_REG_SHIFT(CDNS_MCP_RESP_RDATA); - - return SDW_CMD_OK; -} - -static enum sdw_command_response -_cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd, - int offset, int count, bool defer) -{ - unsigned long time; - u32 base, i, data; - u16 addr; - - /* Program the watermark level for RX FIFO */ - if (cdns->msg_count != count) { - cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, count); - cdns->msg_count = count; - } - - base = CDNS_MCP_CMD_BASE; - addr = msg->addr; - - for (i = 0; i < count; i++) { - data = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR); - data |= cmd << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND); - data |= addr++ << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); - - if (msg->flags == SDW_MSG_FLAG_WRITE) - data |= msg->buf[i + offset]; - - data |= msg->ssp_sync << SDW_REG_SHIFT(CDNS_MCP_CMD_SSP_TAG); - cdns_writel(cdns, base, data); - base += CDNS_MCP_CMD_WORD_LEN; - } - - if (defer) - return SDW_CMD_OK; - - /* wait for timeout or response */ - time = wait_for_completion_timeout(&cdns->tx_complete, - msecs_to_jiffies(CDNS_TX_TIMEOUT)); - if (!time) { - dev_err(cdns->dev, "IO transfer timed out\n"); - msg->len = 0; - return SDW_CMD_TIMEOUT; - } - - return cdns_fill_msg_resp(cdns, msg, count, offset); -} - -static enum sdw_command_response cdns_program_scp_addr( - struct sdw_cdns *cdns, struct sdw_msg *msg) -{ - int nack = 0, no_ack = 0; - unsigned long time; - u32 data[2], base; - int i; - - /* Program the watermark level for RX FIFO */ - if (cdns->msg_count != CDNS_SCP_RX_FIFOLEVEL) { - cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, CDNS_SCP_RX_FIFOLEVEL); - cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL; - } - - data[0] = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR); - data[0] |= 0x3 << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND); - data[1] = data[0]; - - data[0] |= SDW_SCP_ADDRPAGE1 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); - data[1] |= SDW_SCP_ADDRPAGE2 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); - - data[0] |= msg->addr_page1; - data[1] |= msg->addr_page2; - - base = CDNS_MCP_CMD_BASE; - cdns_writel(cdns, base, data[0]); - base += CDNS_MCP_CMD_WORD_LEN; - cdns_writel(cdns, base, data[1]); - - time = wait_for_completion_timeout(&cdns->tx_complete, - msecs_to_jiffies(CDNS_TX_TIMEOUT)); - if (!time) { - dev_err(cdns->dev, "SCP Msg trf timed out\n"); - msg->len = 0; - return SDW_CMD_TIMEOUT; - } - - /* check response the writes */ - for (i = 0; i < 2; i++) { - if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { - no_ack = 1; - dev_err(cdns->dev, "Program SCP Ack not received"); - if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { - nack = 1; - dev_err(cdns->dev, "Program SCP NACK received"); - } - } - } - - /* For NACK, NO ack, don't return err if we are in Broadcast mode */ - if (nack) { - dev_err(cdns->dev, - "SCP_addrpage NACKed for Slave %d", msg->dev_num); - return SDW_CMD_FAIL; - } else if (no_ack) { - dev_dbg(cdns->dev, - "SCP_addrpage ignored for Slave %d", msg->dev_num); - return SDW_CMD_IGNORED; - } - - return SDW_CMD_OK; -} - -static int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd) -{ - int ret; - - if (msg->page) { - ret = cdns_program_scp_addr(cdns, msg); - if (ret) { - msg->len = 0; - return ret; - } - } - - switch (msg->flags) { - case SDW_MSG_FLAG_READ: - *cmd = CDNS_MCP_CMD_READ; - break; - - case SDW_MSG_FLAG_WRITE: - *cmd = CDNS_MCP_CMD_WRITE; - break; - - default: - dev_err(cdns->dev, "Invalid msg cmd: %d\n", msg->flags); - return -EINVAL; - } - - return 0; -} - -enum sdw_command_response -cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - int cmd = 0, ret, i; - - ret = cdns_prep_msg(cdns, msg, &cmd); - if (ret) - return SDW_CMD_FAIL_OTHER; - - for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) { - ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, - CDNS_MCP_CMD_LEN, false); - if (ret < 0) - goto exit; - } - - if (!(msg->len % CDNS_MCP_CMD_LEN)) - goto exit; - - ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, - msg->len % CDNS_MCP_CMD_LEN, false); - -exit: - return ret; -} -EXPORT_SYMBOL(cdns_xfer_msg); - -enum sdw_command_response -cdns_xfer_msg_defer(struct sdw_bus *bus, - struct sdw_msg *msg, struct sdw_defer *defer) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - int cmd = 0, ret; - - /* for defer only 1 message is supported */ - if (msg->len > 1) - return -ENOTSUPP; - - ret = cdns_prep_msg(cdns, msg, &cmd); - if (ret) - return SDW_CMD_FAIL_OTHER; - - cdns->defer = defer; - cdns->defer->length = msg->len; - - return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true); -} -EXPORT_SYMBOL(cdns_xfer_msg_defer); - -enum sdw_command_response -cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - struct sdw_msg msg; - - /* Create dummy message with valid device number */ - memset(&msg, 0, sizeof(msg)); - msg.dev_num = dev_num; - - return cdns_program_scp_addr(cdns, &msg); -} -EXPORT_SYMBOL(cdns_reset_page_addr); - -/* - * IRQ handling - */ - -static void cdns_read_response(struct sdw_cdns *cdns) -{ - u32 num_resp, cmd_base; - int i; - - num_resp = cdns_readl(cdns, CDNS_MCP_FIFOSTAT); - num_resp &= CDNS_MCP_RX_FIFO_AVAIL; - - cmd_base = CDNS_MCP_CMD_BASE; - - for (i = 0; i < num_resp; i++) { - cdns->response_buf[i] = cdns_readl(cdns, cmd_base); - cmd_base += CDNS_MCP_CMD_WORD_LEN; - } -} - -static int cdns_update_slave_status(struct sdw_cdns *cdns, - u32 slave0, u32 slave1) -{ - enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; - bool is_slave = false; - u64 slave, mask; - int i, set_status; - - /* combine the two status */ - slave = ((u64)slave1 << 32) | slave0; - memset(status, 0, sizeof(status)); - - for (i = 0; i <= SDW_MAX_DEVICES; i++) { - mask = (slave >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) & - CDNS_MCP_SLAVE_STATUS_BITS; - if (!mask) - continue; - - is_slave = true; - set_status = 0; - - if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) { - status[i] = SDW_SLAVE_RESERVED; - set_status++; - } - - if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) { - status[i] = SDW_SLAVE_ATTACHED; - set_status++; - } - - if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) { - status[i] = SDW_SLAVE_ALERT; - set_status++; - } - - if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) { - status[i] = SDW_SLAVE_UNATTACHED; - set_status++; - } - - /* first check if Slave reported multiple status */ - if (set_status > 1) { - dev_warn(cdns->dev, - "Slave reported multiple Status: %d\n", - status[i]); - /* - * TODO: we need to reread the status here by - * issuing a PING cmd - */ - } - } - - if (is_slave) - return sdw_handle_slave_status(&cdns->bus, status); - - return 0; -} - -/** - * sdw_cdns_irq() - Cadence interrupt handler - * @irq: irq number - * @dev_id: irq context - */ -irqreturn_t sdw_cdns_irq(int irq, void *dev_id) -{ - struct sdw_cdns *cdns = dev_id; - u32 int_status; - int ret = IRQ_HANDLED; - - /* Check if the link is up */ - if (!cdns->link_up) - return IRQ_NONE; - - int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT); - - if (!(int_status & CDNS_MCP_INT_IRQ)) - return IRQ_NONE; - - if (int_status & CDNS_MCP_INT_RX_WL) { - cdns_read_response(cdns); - - if (cdns->defer) { - cdns_fill_msg_resp(cdns, cdns->defer->msg, - cdns->defer->length, 0); - complete(&cdns->defer->complete); - cdns->defer = NULL; - } else - complete(&cdns->tx_complete); - } - - if (int_status & CDNS_MCP_INT_CTRL_CLASH) { - - /* Slave is driving bit slot during control word */ - dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); - int_status |= CDNS_MCP_INT_CTRL_CLASH; - } - - if (int_status & CDNS_MCP_INT_DATA_CLASH) { - /* - * Multiple slaves trying to drive bit slot, or issue with - * ownership of data bits or Slave gone bonkers - */ - dev_err_ratelimited(cdns->dev, "Bus clash for data word\n"); - int_status |= CDNS_MCP_INT_DATA_CLASH; - } - - if (int_status & CDNS_MCP_INT_SLAVE_MASK) { - /* Mask the Slave interrupt and wake thread */ - cdns_updatel(cdns, CDNS_MCP_INTMASK, - CDNS_MCP_INT_SLAVE_MASK, 0); - - int_status &= ~CDNS_MCP_INT_SLAVE_MASK; - ret = IRQ_WAKE_THREAD; - } - - cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); - return ret; -} -EXPORT_SYMBOL(sdw_cdns_irq); - -/** - * sdw_cdns_thread() - Cadence irq thread handler - * @irq: irq number - * @dev_id: irq context - */ -irqreturn_t sdw_cdns_thread(int irq, void *dev_id) -{ - struct sdw_cdns *cdns = dev_id; - u32 slave0, slave1; - - dev_dbg(cdns->dev, "Slave status change\n"); - - slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); - slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); - - cdns_update_slave_status(cdns, slave0, slave1); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1); - - /* clear and unmask Slave interrupt now */ - cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK); - cdns_updatel(cdns, CDNS_MCP_INTMASK, - CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); - - return IRQ_HANDLED; -} -EXPORT_SYMBOL(sdw_cdns_thread); - -/* - * init routines - */ -static int _cdns_enable_interrupt(struct sdw_cdns *cdns) -{ - u32 mask; - - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, - CDNS_MCP_SLAVE_INTMASK0_MASK); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, - CDNS_MCP_SLAVE_INTMASK1_MASK); - - mask = CDNS_MCP_INT_SLAVE_RSVD | CDNS_MCP_INT_SLAVE_ALERT | - CDNS_MCP_INT_SLAVE_ATTACH | CDNS_MCP_INT_SLAVE_NATTACH | - CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | - CDNS_MCP_INT_RX_WL | CDNS_MCP_INT_IRQ | CDNS_MCP_INT_DPINT; - - cdns_writel(cdns, CDNS_MCP_INTMASK, mask); - - return 0; -} - -/** - * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config - * @cdns: Cadence instance - */ -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) -{ - int ret; - - _cdns_enable_interrupt(cdns); - ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT); - if (ret < 0) - dev_err(cdns->dev, "Config update timedout"); - - return ret; -} -EXPORT_SYMBOL(sdw_cdns_enable_interrupt); - -static int cdns_allocate_pdi(struct sdw_cdns *cdns, - struct sdw_cdns_pdi **stream, - u32 num, u32 pdi_offset) -{ - struct sdw_cdns_pdi *pdi; - int i; - - if (!num) - return 0; - - pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL); - if (!pdi) - return -ENOMEM; - - for (i = 0; i < num; i++) { - pdi[i].num = i + pdi_offset; - pdi[i].assigned = false; - } - - *stream = pdi; - return 0; -} - -/** - * sdw_cdns_pdi_init() - PDI initialization routine - * - * @cdns: Cadence instance - * @config: Stream configurations - */ -int sdw_cdns_pdi_init(struct sdw_cdns *cdns, - struct sdw_cdns_stream_config config) -{ - struct sdw_cdns_streams *stream; - int offset, i, ret; - - cdns->pcm.num_bd = config.pcm_bd; - cdns->pcm.num_in = config.pcm_in; - cdns->pcm.num_out = config.pcm_out; - cdns->pdm.num_bd = config.pdm_bd; - cdns->pdm.num_in = config.pdm_in; - cdns->pdm.num_out = config.pdm_out; - - /* Allocate PDIs for PCMs */ - stream = &cdns->pcm; - - /* First two PDIs are reserved for bulk transfers */ - stream->num_bd -= CDNS_PCM_PDI_OFFSET; - offset = CDNS_PCM_PDI_OFFSET; - - ret = cdns_allocate_pdi(cdns, &stream->bd, - stream->num_bd, offset); - if (ret) - return ret; - - offset += stream->num_bd; - - ret = cdns_allocate_pdi(cdns, &stream->in, - stream->num_in, offset); - if (ret) - return ret; - - offset += stream->num_in; - - ret = cdns_allocate_pdi(cdns, &stream->out, - stream->num_out, offset); - if (ret) - return ret; - - /* Update total number of PCM PDIs */ - stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; - cdns->num_ports = stream->num_pdi; - - /* Allocate PDIs for PDMs */ - stream = &cdns->pdm; - offset = CDNS_PDM_PDI_OFFSET; - ret = cdns_allocate_pdi(cdns, &stream->bd, - stream->num_bd, offset); - if (ret) - return ret; - - offset += stream->num_bd; - - ret = cdns_allocate_pdi(cdns, &stream->in, - stream->num_in, offset); - if (ret) - return ret; - - offset += stream->num_in; - - ret = cdns_allocate_pdi(cdns, &stream->out, - stream->num_out, offset); - if (ret) - return ret; - - /* Update total number of PDM PDIs */ - stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; - cdns->num_ports += stream->num_pdi; - - cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports, - sizeof(*cdns->ports), GFP_KERNEL); - if (!cdns->ports) { - ret = -ENOMEM; - return ret; - } - - for (i = 0; i < cdns->num_ports; i++) { - cdns->ports[i].assigned = false; - cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */ - } - - return 0; -} -EXPORT_SYMBOL(sdw_cdns_pdi_init); - -/** - * sdw_cdns_init() - Cadence initialization - * @cdns: Cadence instance - */ -int sdw_cdns_init(struct sdw_cdns *cdns) -{ - u32 val; - int ret; - - /* Exit clock stop */ - ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CLK_STOP_CLR); - if (ret < 0) { - dev_err(cdns->dev, "Couldn't exit from clock stop\n"); - return ret; - } - - /* Set clock divider */ - val = cdns_readl(cdns, CDNS_MCP_CLK_CTRL0); - val |= CDNS_DEFAULT_CLK_DIVIDER; - cdns_writel(cdns, CDNS_MCP_CLK_CTRL0, val); - - /* Set the default frame shape */ - cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, CDNS_DEFAULT_FRAME_SHAPE); - - /* Set SSP interval to default value */ - cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); - cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL); - - /* Set cmd accept mode */ - cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, - CDNS_MCP_CONTROL_CMD_ACCEPT); - - /* Configure mcp config */ - val = cdns_readl(cdns, CDNS_MCP_CONFIG); - - /* Set Max cmd retry to 15 */ - val |= CDNS_MCP_CONFIG_MCMD_RETRY; - - /* Set frame delay between PREQ and ping frame to 15 frames */ - val |= 0xF << SDW_REG_SHIFT(CDNS_MCP_CONFIG_MPREQ_DELAY); - - /* Disable auto bus release */ - val &= ~CDNS_MCP_CONFIG_BUS_REL; - - /* Disable sniffer mode */ - val &= ~CDNS_MCP_CONFIG_SNIFFER; - - /* Set cmd mode for Tx and Rx cmds */ - val &= ~CDNS_MCP_CONFIG_CMD; - - /* Set operation to normal */ - val &= ~CDNS_MCP_CONFIG_OP; - val |= CDNS_MCP_CONFIG_OP_NORMAL; - - cdns_writel(cdns, CDNS_MCP_CONFIG, val); - - return 0; -} -EXPORT_SYMBOL(sdw_cdns_init); - -int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - int mcp_clkctrl_off, mcp_clkctrl; - int divider; - - if (!params->curr_dr_freq) { - dev_err(cdns->dev, "NULL curr_dr_freq"); - return -EINVAL; - } - - divider = (params->max_dr_freq / params->curr_dr_freq) - 1; - - if (params->next_bank) - mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; - else - mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; - - mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); - mcp_clkctrl |= divider; - cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); - - return 0; -} -EXPORT_SYMBOL(cdns_bus_conf); - -static int cdns_port_params(struct sdw_bus *bus, - struct sdw_port_params *p_params, unsigned int bank) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - int dpn_config = 0, dpn_config_off; - - if (bank) - dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num); - else - dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num); - - dpn_config = cdns_readl(cdns, dpn_config_off); - - dpn_config |= ((p_params->bps - 1) << - SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL)); - dpn_config |= (p_params->flow_mode << - SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW)); - dpn_config |= (p_params->data_mode << - SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT)); - - cdns_writel(cdns, dpn_config_off, dpn_config); - - return 0; -} - -static int cdns_transport_params(struct sdw_bus *bus, - struct sdw_transport_params *t_params, - enum sdw_reg_bank bank) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - int dpn_offsetctrl = 0, dpn_offsetctrl_off; - int dpn_config = 0, dpn_config_off; - int dpn_hctrl = 0, dpn_hctrl_off; - int num = t_params->port_num; - int dpn_samplectrl_off; - - /* - * Note: Only full data port is supported on the Master side for - * both PCM and PDM ports. - */ - - if (bank) { - dpn_config_off = CDNS_DPN_B1_CONFIG(num); - dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num); - dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num); - dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num); - } else { - dpn_config_off = CDNS_DPN_B0_CONFIG(num); - dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num); - dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num); - dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num); - } - - dpn_config = cdns_readl(cdns, dpn_config_off); - - dpn_config |= (t_params->blk_grp_ctrl << - SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC)); - dpn_config |= (t_params->blk_pkg_mode << - SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM)); - cdns_writel(cdns, dpn_config_off, dpn_config); - - dpn_offsetctrl |= (t_params->offset1 << - SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1)); - dpn_offsetctrl |= (t_params->offset2 << - SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2)); - cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); - - dpn_hctrl |= (t_params->hstart << - SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART)); - dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP)); - dpn_hctrl |= (t_params->lane_ctrl << - SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL)); - - cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); - cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); - - return 0; -} - -static int cdns_port_enable(struct sdw_bus *bus, - struct sdw_enable_ch *enable_ch, unsigned int bank) -{ - struct sdw_cdns *cdns = bus_to_cdns(bus); - int dpn_chnen_off, ch_mask; - - if (bank) - dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->port_num); - else - dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->port_num); - - ch_mask = enable_ch->ch_mask * enable_ch->enable; - cdns_writel(cdns, dpn_chnen_off, ch_mask); - - return 0; -} - -static const struct sdw_master_port_ops cdns_port_ops = { - .dpn_set_port_params = cdns_port_params, - .dpn_set_port_transport_params = cdns_transport_params, - .dpn_port_enable_ch = cdns_port_enable, -}; - -/** - * sdw_cdns_probe() - Cadence probe routine - * @cdns: Cadence instance - */ -int sdw_cdns_probe(struct sdw_cdns *cdns) -{ - init_completion(&cdns->tx_complete); - cdns->bus.port_ops = &cdns_port_ops; - - return 0; -} -EXPORT_SYMBOL(sdw_cdns_probe); - -int cdns_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, bool pcm, int direction) -{ - struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); - struct sdw_cdns_dma_data *dma; - - dma = kzalloc(sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; - - if (pcm) - dma->stream_type = SDW_STREAM_PCM; - else - dma->stream_type = SDW_STREAM_PDM; - - dma->bus = &cdns->bus; - dma->link_id = cdns->instance; - - dma->stream = stream; - - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = dma; - else - dai->capture_dma_data = dma; - - return 0; -} -EXPORT_SYMBOL(cdns_set_sdw_stream); - -/** - * cdns_find_pdi() - Find a free PDI - * - * @cdns: Cadence instance - * @num: Number of PDIs - * @pdi: PDI instances - * - * Find and return a free PDI for a given PDI array - */ -static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, - unsigned int num, struct sdw_cdns_pdi *pdi) -{ - int i; - - for (i = 0; i < num; i++) { - if (pdi[i].assigned == true) - continue; - pdi[i].assigned = true; - return &pdi[i]; - } - - return NULL; -} - -/** - * sdw_cdns_config_stream: Configure a stream - * - * @cdns: Cadence instance - * @port: Cadence data port - * @ch: Channel count - * @dir: Data direction - * @pdi: PDI to be used - */ -void sdw_cdns_config_stream(struct sdw_cdns *cdns, - struct sdw_cdns_port *port, - u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) -{ - u32 offset, val = 0; - - if (dir == SDW_DATA_DIR_RX) - val = CDNS_PORTCTRL_DIRN; - - offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET; - cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val); - - val = port->num; - val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL); - cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val); -} -EXPORT_SYMBOL(sdw_cdns_config_stream); - -/** - * cdns_get_num_pdi() - Get number of PDIs required - * - * @cdns: Cadence instance - * @pdi: PDI to be used - * @num: Number of PDIs - * @ch_count: Channel count - */ -static int cdns_get_num_pdi(struct sdw_cdns *cdns, - struct sdw_cdns_pdi *pdi, - unsigned int num, u32 ch_count) -{ - int i, pdis = 0; - - for (i = 0; i < num; i++) { - if (pdi[i].assigned == true) - continue; - - if (pdi[i].ch_count < ch_count) - ch_count -= pdi[i].ch_count; - else - ch_count = 0; - - pdis++; - - if (!ch_count) - break; - } - - if (ch_count) - return 0; - - return pdis; -} - -/** - * sdw_cdns_get_stream() - Get stream information - * - * @cdns: Cadence instance - * @stream: Stream to be allocated - * @ch: Channel count - * @dir: Data direction - */ -int sdw_cdns_get_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - u32 ch, u32 dir) -{ - int pdis = 0; - - if (dir == SDW_DATA_DIR_RX) - pdis = cdns_get_num_pdi(cdns, stream->in, stream->num_in, ch); - else - pdis = cdns_get_num_pdi(cdns, stream->out, stream->num_out, ch); - - /* check if we found PDI, else find in bi-directional */ - if (!pdis) - pdis = cdns_get_num_pdi(cdns, stream->bd, stream->num_bd, ch); - - return pdis; -} -EXPORT_SYMBOL(sdw_cdns_get_stream); - -/** - * sdw_cdns_alloc_stream() - Allocate a stream - * - * @cdns: Cadence instance - * @stream: Stream to be allocated - * @port: Cadence data port - * @ch: Channel count - * @dir: Data direction - */ -int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - struct sdw_cdns_port *port, u32 ch, u32 dir) -{ - struct sdw_cdns_pdi *pdi = NULL; - - if (dir == SDW_DATA_DIR_RX) - pdi = cdns_find_pdi(cdns, stream->num_in, stream->in); - else - pdi = cdns_find_pdi(cdns, stream->num_out, stream->out); - - /* check if we found a PDI, else find in bi-directional */ - if (!pdi) - pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd); - - if (!pdi) - return -EIO; - - port->pdi = pdi; - pdi->l_ch_num = 0; - pdi->h_ch_num = ch - 1; - pdi->dir = dir; - pdi->ch_count = ch; - - return 0; -} -EXPORT_SYMBOL(sdw_cdns_alloc_stream); - -void sdw_cdns_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct sdw_cdns_dma_data *dma; - - dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return; - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(dma); -} -EXPORT_SYMBOL(sdw_cdns_shutdown); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("Cadence Soundwire Library"); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h deleted file mode 100644 index eb902b19c5a4..000000000000 --- a/drivers/soundwire/cadence_master.h +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. -#include - -#ifndef __SDW_CADENCE_H -#define __SDW_CADENCE_H - -/** - * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance - * - * @assigned: pdi assigned - * @num: pdi number - * @intel_alh_id: link identifier - * @l_ch_num: low channel for PDI - * @h_ch_num: high channel for PDI - * @ch_count: total channel count for PDI - * @dir: data direction - * @type: stream type, PDM or PCM - */ -struct sdw_cdns_pdi { - bool assigned; - int num; - int intel_alh_id; - int l_ch_num; - int h_ch_num; - int ch_count; - enum sdw_data_direction dir; - enum sdw_stream_type type; -}; - -/** - * struct sdw_cdns_port: Cadence port structure - * - * @num: port number - * @assigned: port assigned - * @ch: channel count - * @direction: data port direction - * @pdi: pdi for this port - */ -struct sdw_cdns_port { - unsigned int num; - bool assigned; - unsigned int ch; - enum sdw_data_direction direction; - struct sdw_cdns_pdi *pdi; -}; - -/** - * struct sdw_cdns_streams: Cadence stream data structure - * - * @num_bd: number of bidirectional streams - * @num_in: number of input streams - * @num_out: number of output streams - * @num_ch_bd: number of bidirectional stream channels - * @num_ch_bd: number of input stream channels - * @num_ch_bd: number of output stream channels - * @num_pdi: total number of PDIs - * @bd: bidirectional streams - * @in: input streams - * @out: output streams - */ -struct sdw_cdns_streams { - unsigned int num_bd; - unsigned int num_in; - unsigned int num_out; - unsigned int num_ch_bd; - unsigned int num_ch_in; - unsigned int num_ch_out; - unsigned int num_pdi; - struct sdw_cdns_pdi *bd; - struct sdw_cdns_pdi *in; - struct sdw_cdns_pdi *out; -}; - -/** - * struct sdw_cdns_stream_config: stream configuration - * - * @pcm_bd: number of bidirectional PCM streams supported - * @pcm_in: number of input PCM streams supported - * @pcm_out: number of output PCM streams supported - * @pdm_bd: number of bidirectional PDM streams supported - * @pdm_in: number of input PDM streams supported - * @pdm_out: number of output PDM streams supported - */ -struct sdw_cdns_stream_config { - unsigned int pcm_bd; - unsigned int pcm_in; - unsigned int pcm_out; - unsigned int pdm_bd; - unsigned int pdm_in; - unsigned int pdm_out; -}; - -/** - * struct sdw_cdns_dma_data: Cadence DMA data - * - * @name: SoundWire stream name - * @nr_ports: Number of ports - * @port: Ports - * @bus: Bus handle - * @stream_type: Stream type - * @link_id: Master link id - */ -struct sdw_cdns_dma_data { - char *name; - struct sdw_stream_runtime *stream; - int nr_ports; - struct sdw_cdns_port **port; - struct sdw_bus *bus; - enum sdw_stream_type stream_type; - int link_id; -}; - -/** - * struct sdw_cdns - Cadence driver context - * @dev: Linux device - * @bus: Bus handle - * @instance: instance number - * @response_buf: SoundWire response buffer - * @tx_complete: Tx completion - * @defer: Defer pointer - * @ports: Data ports - * @num_ports: Total number of data ports - * @pcm: PCM streams - * @pdm: PDM streams - * @registers: Cadence registers - * @link_up: Link status - * @msg_count: Messages sent on bus - */ -struct sdw_cdns { - struct device *dev; - struct sdw_bus bus; - unsigned int instance; - - u32 response_buf[0x80]; - struct completion tx_complete; - struct sdw_defer *defer; - - struct sdw_cdns_port *ports; - int num_ports; - - struct sdw_cdns_streams pcm; - struct sdw_cdns_streams pdm; - - void __iomem *registers; - - bool link_up; - unsigned int msg_count; -}; - -#define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus) - -/* Exported symbols */ - -int sdw_cdns_probe(struct sdw_cdns *cdns); -extern struct sdw_master_ops sdw_cdns_master_ops; - -irqreturn_t sdw_cdns_irq(int irq, void *dev_id); -irqreturn_t sdw_cdns_thread(int irq, void *dev_id); - -int sdw_cdns_init(struct sdw_cdns *cdns); -int sdw_cdns_pdi_init(struct sdw_cdns *cdns, - struct sdw_cdns_stream_config config); -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); - -int sdw_cdns_get_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - u32 ch, u32 dir); -int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - struct sdw_cdns_port *port, u32 ch, u32 dir); -void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port, - u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); - -void sdw_cdns_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai); -int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai, - void *stream, int direction); -int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai, - void *stream, int direction); - -enum sdw_command_response -cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); - -enum sdw_command_response -cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg); - -enum sdw_command_response -cdns_xfer_msg_defer(struct sdw_bus *bus, - struct sdw_msg *msg, struct sdw_defer *defer); - -enum sdw_command_response -cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); - -int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); - -int cdns_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, bool pcm, int direction); -#endif /* __SDW_CADENCE_H */ diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c deleted file mode 100644 index e49d3c810677..000000000000 --- a/drivers/soundwire/intel.c +++ /dev/null @@ -1,882 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -/* - * Soundwire Intel Master Driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cadence_master.h" -#include "intel.h" - -/* Intel SHIM Registers Definition */ -#define SDW_SHIM_LCAP 0x0 -#define SDW_SHIM_LCTL 0x4 -#define SDW_SHIM_IPPTR 0x8 -#define SDW_SHIM_SYNC 0xC - -#define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * x) -#define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * x) -#define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * x) -#define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * x) -#define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * x) -#define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * x) - -#define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * x) + (0x2 * y)) -#define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * x) + (0x2 * y)) -#define SDW_SHIM_PDMSCAP(x) (0x062 + 0x60 * x) -#define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * x) -#define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * x) - -#define SDW_SHIM_WAKEEN 0x190 -#define SDW_SHIM_WAKESTS 0x192 - -#define SDW_SHIM_LCTL_SPA BIT(0) -#define SDW_SHIM_LCTL_CPA BIT(8) - -#define SDW_SHIM_SYNC_SYNCPRD_VAL 0x176F -#define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0) -#define SDW_SHIM_SYNC_SYNCCPU BIT(15) -#define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16) -#define SDW_SHIM_SYNC_CMDSYNC BIT(16) -#define SDW_SHIM_SYNC_SYNCGO BIT(24) - -#define SDW_SHIM_PCMSCAP_ISS GENMASK(3, 0) -#define SDW_SHIM_PCMSCAP_OSS GENMASK(7, 4) -#define SDW_SHIM_PCMSCAP_BSS GENMASK(12, 8) - -#define SDW_SHIM_PCMSYCM_LCHN GENMASK(3, 0) -#define SDW_SHIM_PCMSYCM_HCHN GENMASK(7, 4) -#define SDW_SHIM_PCMSYCM_STREAM GENMASK(13, 8) -#define SDW_SHIM_PCMSYCM_DIR BIT(15) - -#define SDW_SHIM_PDMSCAP_ISS GENMASK(3, 0) -#define SDW_SHIM_PDMSCAP_OSS GENMASK(7, 4) -#define SDW_SHIM_PDMSCAP_BSS GENMASK(12, 8) -#define SDW_SHIM_PDMSCAP_CPSS GENMASK(15, 13) - -#define SDW_SHIM_IOCTL_MIF BIT(0) -#define SDW_SHIM_IOCTL_CO BIT(1) -#define SDW_SHIM_IOCTL_COE BIT(2) -#define SDW_SHIM_IOCTL_DO BIT(3) -#define SDW_SHIM_IOCTL_DOE BIT(4) -#define SDW_SHIM_IOCTL_BKE BIT(5) -#define SDW_SHIM_IOCTL_WPDD BIT(6) -#define SDW_SHIM_IOCTL_CIBD BIT(8) -#define SDW_SHIM_IOCTL_DIBD BIT(9) - -#define SDW_SHIM_CTMCTL_DACTQE BIT(0) -#define SDW_SHIM_CTMCTL_DODS BIT(1) -#define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3) - -#define SDW_SHIM_WAKEEN_ENABLE BIT(0) -#define SDW_SHIM_WAKESTS_STATUS BIT(0) - -/* Intel ALH Register definitions */ -#define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * x)) - -#define SDW_ALH_STRMZCFG_DMAT_VAL 0x3 -#define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) -#define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) - -enum intel_pdi_type { - INTEL_PDI_IN = 0, - INTEL_PDI_OUT = 1, - INTEL_PDI_BD = 2, -}; - -struct sdw_intel { - struct sdw_cdns cdns; - int instance; - struct sdw_intel_link_res *res; -}; - -#define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) - -/* - * Read, write helpers for HW registers - */ -static inline int intel_readl(void __iomem *base, int offset) -{ - return readl(base + offset); -} - -static inline void intel_writel(void __iomem *base, int offset, int value) -{ - writel(value, base + offset); -} - -static inline u16 intel_readw(void __iomem *base, int offset) -{ - return readw(base + offset); -} - -static inline void intel_writew(void __iomem *base, int offset, u16 value) -{ - writew(value, base + offset); -} - -static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) -{ - int timeout = 10; - u32 reg_read; - - writel(value, base + offset); - do { - reg_read = readl(base + offset); - if (!(reg_read & mask)) - return 0; - - timeout--; - udelay(50); - } while (timeout != 0); - - return -EAGAIN; -} - -static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) -{ - int timeout = 10; - u32 reg_read; - - writel(value, base + offset); - do { - reg_read = readl(base + offset); - if (reg_read & mask) - return 0; - - timeout--; - udelay(50); - } while (timeout != 0); - - return -EAGAIN; -} - -/* - * shim ops - */ - -static int intel_link_power_up(struct sdw_intel *sdw) -{ - unsigned int link_id = sdw->instance; - void __iomem *shim = sdw->res->shim; - int spa_mask, cpa_mask; - int link_control, ret; - - /* Link power up sequence */ - link_control = intel_readl(shim, SDW_SHIM_LCTL); - spa_mask = (SDW_SHIM_LCTL_SPA << link_id); - cpa_mask = (SDW_SHIM_LCTL_CPA << link_id); - link_control |= spa_mask; - - ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); - if (ret < 0) - return ret; - - sdw->cdns.link_up = true; - return 0; -} - -static int intel_shim_init(struct sdw_intel *sdw) -{ - void __iomem *shim = sdw->res->shim; - unsigned int link_id = sdw->instance; - int sync_reg, ret; - u16 ioctl = 0, act = 0; - - /* Initialize Shim */ - ioctl |= SDW_SHIM_IOCTL_BKE; - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - ioctl |= SDW_SHIM_IOCTL_WPDD; - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - ioctl |= SDW_SHIM_IOCTL_DO; - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - ioctl |= SDW_SHIM_IOCTL_DOE; - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - /* Switch to MIP from Glue logic */ - ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); - - ioctl &= ~(SDW_SHIM_IOCTL_DOE); - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - ioctl &= ~(SDW_SHIM_IOCTL_DO); - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - ioctl |= (SDW_SHIM_IOCTL_MIF); - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - ioctl &= ~(SDW_SHIM_IOCTL_BKE); - ioctl &= ~(SDW_SHIM_IOCTL_COE); - - intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); - - act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS); - act |= SDW_SHIM_CTMCTL_DACTQE; - act |= SDW_SHIM_CTMCTL_DODS; - intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); - - /* Now set SyncPRD period */ - sync_reg = intel_readl(shim, SDW_SHIM_SYNC); - sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL << - SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD)); - - /* Set SyncCPU bit */ - sync_reg |= SDW_SHIM_SYNC_SYNCCPU; - ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, - SDW_SHIM_SYNC_SYNCCPU); - if (ret < 0) - dev_err(sdw->cdns.dev, "Failed to set sync period: %d", ret); - - return ret; -} - -/* - * PDI routines - */ -static void intel_pdi_init(struct sdw_intel *sdw, - struct sdw_cdns_stream_config *config) -{ - void __iomem *shim = sdw->res->shim; - unsigned int link_id = sdw->instance; - int pcm_cap, pdm_cap; - - /* PCM Stream Capability */ - pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); - - config->pcm_bd = (pcm_cap & SDW_SHIM_PCMSCAP_BSS) >> - SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_BSS); - config->pcm_in = (pcm_cap & SDW_SHIM_PCMSCAP_ISS) >> - SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_ISS); - config->pcm_out = (pcm_cap & SDW_SHIM_PCMSCAP_OSS) >> - SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_OSS); - - /* PDM Stream Capability */ - pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); - - config->pdm_bd = (pdm_cap & SDW_SHIM_PDMSCAP_BSS) >> - SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_BSS); - config->pdm_in = (pdm_cap & SDW_SHIM_PDMSCAP_ISS) >> - SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_ISS); - config->pdm_out = (pdm_cap & SDW_SHIM_PDMSCAP_OSS) >> - SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_OSS); -} - -static int -intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) -{ - void __iomem *shim = sdw->res->shim; - unsigned int link_id = sdw->instance; - int count; - - if (pcm) { - count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); - - /* - * WORKAROUND: on all existing Intel controllers, pdi - * number 2 reports channel count as 1 even though it - * supports 8 channels. Performing hardcoding for pdi - * number 2. - */ - if (pdi_num == 2) - count = 7; - - } else { - count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); - count = ((count & SDW_SHIM_PDMSCAP_CPSS) >> - SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_CPSS)); - } - - /* zero based values for channel count in register */ - count++; - - return count; -} - -static int intel_pdi_get_ch_update(struct sdw_intel *sdw, - struct sdw_cdns_pdi *pdi, - unsigned int num_pdi, - unsigned int *num_ch, bool pcm) -{ - int i, ch_count = 0; - - for (i = 0; i < num_pdi; i++) { - pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm); - ch_count += pdi->ch_count; - pdi++; - } - - *num_ch = ch_count; - return 0; -} - -static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, - struct sdw_cdns_streams *stream, bool pcm) -{ - intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, - &stream->num_ch_bd, pcm); - - intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, - &stream->num_ch_in, pcm); - - intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, - &stream->num_ch_out, pcm); - - return 0; -} - -static int intel_pdi_ch_update(struct sdw_intel *sdw) -{ - /* First update PCM streams followed by PDM streams */ - intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true); - intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false); - - return 0; -} - -static void -intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) -{ - void __iomem *shim = sdw->res->shim; - unsigned int link_id = sdw->instance; - int pdi_conf = 0; - - /* the Bulk and PCM streams are not contiguous */ - pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; - if (pdi->num >= 2) - pdi->intel_alh_id += 2; - - /* - * Program stream parameters to stream SHIM register - * This is applicable for PCM stream only. - */ - if (pdi->type != SDW_STREAM_PCM) - return; - - if (pdi->dir == SDW_DATA_DIR_RX) - pdi_conf |= SDW_SHIM_PCMSYCM_DIR; - else - pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); - - pdi_conf |= (pdi->intel_alh_id << - SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_STREAM)); - pdi_conf |= (pdi->l_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_LCHN)); - pdi_conf |= (pdi->h_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_HCHN)); - - intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); -} - -static void -intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) -{ - void __iomem *alh = sdw->res->alh; - unsigned int link_id = sdw->instance; - unsigned int conf; - - /* the Bulk and PCM streams are not contiguous */ - pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; - if (pdi->num >= 2) - pdi->intel_alh_id += 2; - - /* Program Stream config ALH register */ - conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); - - conf |= (SDW_ALH_STRMZCFG_DMAT_VAL << - SDW_REG_SHIFT(SDW_ALH_STRMZCFG_DMAT)); - - conf |= ((pdi->ch_count - 1) << - SDW_REG_SHIFT(SDW_ALH_STRMZCFG_CHN)); - - intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); -} - -static int intel_config_stream(struct sdw_intel *sdw, - struct snd_pcm_substream *substream, - struct snd_soc_dai *dai, - struct snd_pcm_hw_params *hw_params, int link_id) -{ - if (sdw->res->ops && sdw->res->ops->config_stream) - return sdw->res->ops->config_stream(sdw->res->arg, - substream, dai, hw_params, link_id); - - return -EIO; -} - -/* - * DAI routines - */ - -static struct sdw_cdns_port *intel_alloc_port(struct sdw_intel *sdw, - u32 ch, u32 dir, bool pcm) -{ - struct sdw_cdns *cdns = &sdw->cdns; - struct sdw_cdns_port *port = NULL; - int i, ret = 0; - - for (i = 0; i < cdns->num_ports; i++) { - if (cdns->ports[i].assigned == true) - continue; - - port = &cdns->ports[i]; - port->assigned = true; - port->direction = dir; - port->ch = ch; - break; - } - - if (!port) { - dev_err(cdns->dev, "Unable to find a free port\n"); - return NULL; - } - - if (pcm) { - ret = sdw_cdns_alloc_stream(cdns, &cdns->pcm, port, ch, dir); - if (ret) - goto out; - - intel_pdi_shim_configure(sdw, port->pdi); - sdw_cdns_config_stream(cdns, port, ch, dir, port->pdi); - - intel_pdi_alh_configure(sdw, port->pdi); - - } else { - ret = sdw_cdns_alloc_stream(cdns, &cdns->pdm, port, ch, dir); - } - -out: - if (ret) { - port->assigned = false; - port = NULL; - } - - return port; -} - -static void intel_port_cleanup(struct sdw_cdns_dma_data *dma) -{ - int i; - - for (i = 0; i < dma->nr_ports; i++) { - if (dma->port[i]) { - dma->port[i]->pdi->assigned = false; - dma->port[i]->pdi = NULL; - dma->port[i]->assigned = false; - dma->port[i] = NULL; - } - } -} - -static int intel_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); - struct sdw_intel *sdw = cdns_to_intel(cdns); - struct sdw_cdns_dma_data *dma; - struct sdw_stream_config sconfig; - struct sdw_port_config *pconfig; - int ret, i, ch, dir; - bool pcm = true; - - dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return -EIO; - - ch = params_channels(params); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - dir = SDW_DATA_DIR_RX; - else - dir = SDW_DATA_DIR_TX; - - if (dma->stream_type == SDW_STREAM_PDM) { - /* TODO: Check whether PDM decimator is already in use */ - dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pdm, ch, dir); - pcm = false; - } else { - dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pcm, ch, dir); - } - - if (!dma->nr_ports) { - dev_err(dai->dev, "ports/resources not available"); - return -EINVAL; - } - - dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL); - if (!dma->port) - return -ENOMEM; - - for (i = 0; i < dma->nr_ports; i++) { - dma->port[i] = intel_alloc_port(sdw, ch, dir, pcm); - if (!dma->port[i]) { - ret = -EINVAL; - goto port_error; - } - } - - /* Inform DSP about PDI stream number */ - for (i = 0; i < dma->nr_ports; i++) { - ret = intel_config_stream(sdw, substream, dai, params, - dma->port[i]->pdi->intel_alh_id); - if (ret) - goto port_error; - } - - sconfig.direction = dir; - sconfig.ch_count = ch; - sconfig.frame_rate = params_rate(params); - sconfig.type = dma->stream_type; - - if (dma->stream_type == SDW_STREAM_PDM) { - sconfig.frame_rate *= 50; - sconfig.bps = 1; - } else { - sconfig.bps = snd_pcm_format_width(params_format(params)); - } - - /* Port configuration */ - pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL); - if (!pconfig) { - ret = -ENOMEM; - goto port_error; - } - - for (i = 0; i < dma->nr_ports; i++) { - pconfig[i].num = dma->port[i]->num; - pconfig[i].ch_mask = (1 << ch) - 1; - } - - ret = sdw_stream_add_master(&cdns->bus, &sconfig, - pconfig, dma->nr_ports, dma->stream); - if (ret) { - dev_err(cdns->dev, "add master to stream failed:%d", ret); - goto stream_error; - } - - kfree(pconfig); - return ret; - -stream_error: - kfree(pconfig); -port_error: - intel_port_cleanup(dma); - kfree(dma->port); - return ret; -} - -static int -intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); - struct sdw_cdns_dma_data *dma; - int ret; - - dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return -EIO; - - ret = sdw_stream_remove_master(&cdns->bus, dma->stream); - if (ret < 0) - dev_err(dai->dev, "remove master from stream %s failed: %d", - dma->stream->name, ret); - - intel_port_cleanup(dma); - kfree(dma->port); - return ret; -} - -static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, int direction) -{ - return cdns_set_sdw_stream(dai, stream, true, direction); -} - -static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, - void *stream, int direction) -{ - return cdns_set_sdw_stream(dai, stream, false, direction); -} - -static struct snd_soc_dai_ops intel_pcm_dai_ops = { - .hw_params = intel_hw_params, - .hw_free = intel_hw_free, - .shutdown = sdw_cdns_shutdown, - .set_sdw_stream = intel_pcm_set_sdw_stream, -}; - -static struct snd_soc_dai_ops intel_pdm_dai_ops = { - .hw_params = intel_hw_params, - .hw_free = intel_hw_free, - .shutdown = sdw_cdns_shutdown, - .set_sdw_stream = intel_pdm_set_sdw_stream, -}; - -static const struct snd_soc_component_driver dai_component = { - .name = "soundwire", -}; - -static int intel_create_dai(struct sdw_cdns *cdns, - struct snd_soc_dai_driver *dais, - enum intel_pdi_type type, - u32 num, u32 off, u32 max_ch, bool pcm) -{ - int i; - - if (num == 0) - return 0; - - /* TODO: Read supported rates/formats from hardware */ - for (i = off; i < (off + num); i++) { - dais[i].name = kasprintf(GFP_KERNEL, "SDW%d Pin%d", - cdns->instance, i); - if (!dais[i].name) - return -ENOMEM; - - if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { - dais[i].playback.stream_name = kasprintf(GFP_KERNEL, - "SDW%d Tx%d", - cdns->instance, i); - if (!dais[i].playback.stream_name) { - kfree(dais[i].name); - return -ENOMEM; - } - - dais[i].playback.channels_min = 1; - dais[i].playback.channels_max = max_ch; - dais[i].playback.rates = SNDRV_PCM_RATE_48000; - dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; - } - - if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { - dais[i].capture.stream_name = kasprintf(GFP_KERNEL, - "SDW%d Rx%d", - cdns->instance, i); - if (!dais[i].capture.stream_name) { - kfree(dais[i].name); - kfree(dais[i].playback.stream_name); - return -ENOMEM; - } - - dais[i].capture.channels_min = 1; - dais[i].capture.channels_max = max_ch; - dais[i].capture.rates = SNDRV_PCM_RATE_48000; - dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; - } - - dais[i].id = SDW_DAI_ID_RANGE_START + i; - - if (pcm) - dais[i].ops = &intel_pcm_dai_ops; - else - dais[i].ops = &intel_pdm_dai_ops; - } - - return 0; -} - -static int intel_register_dai(struct sdw_intel *sdw) -{ - struct sdw_cdns *cdns = &sdw->cdns; - struct sdw_cdns_streams *stream; - struct snd_soc_dai_driver *dais; - int num_dai, ret, off = 0; - - /* DAIs are created based on total number of PDIs supported */ - num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; - - dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); - if (!dais) - return -ENOMEM; - - /* Create PCM DAIs */ - stream = &cdns->pcm; - - ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, - stream->num_in, off, stream->num_ch_in, true); - if (ret) - return ret; - - off += cdns->pcm.num_in; - ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, - cdns->pcm.num_out, off, stream->num_ch_out, true); - if (ret) - return ret; - - off += cdns->pcm.num_out; - ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, - cdns->pcm.num_bd, off, stream->num_ch_bd, true); - if (ret) - return ret; - - /* Create PDM DAIs */ - stream = &cdns->pdm; - off += cdns->pcm.num_bd; - ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, - cdns->pdm.num_in, off, stream->num_ch_in, false); - if (ret) - return ret; - - off += cdns->pdm.num_in; - ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, - cdns->pdm.num_out, off, stream->num_ch_out, false); - if (ret) - return ret; - - off += cdns->pdm.num_bd; - ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, - cdns->pdm.num_bd, off, stream->num_ch_bd, false); - if (ret) - return ret; - - return snd_soc_register_component(cdns->dev, &dai_component, - dais, num_dai); -} - -static int intel_prop_read(struct sdw_bus *bus) -{ - /* Initialize with default handler to read all DisCo properties */ - sdw_master_read_prop(bus); - - /* BIOS is not giving some values correctly. So, lets override them */ - bus->prop.num_freq = 1; - bus->prop.freq = devm_kcalloc(bus->dev, sizeof(*bus->prop.freq), - bus->prop.num_freq, GFP_KERNEL); - if (!bus->prop.freq) - return -ENOMEM; - - bus->prop.freq[0] = bus->prop.max_freq; - bus->prop.err_threshold = 5; - - return 0; -} - -static struct sdw_master_ops sdw_intel_ops = { - .read_prop = sdw_master_read_prop, - .xfer_msg = cdns_xfer_msg, - .xfer_msg_defer = cdns_xfer_msg_defer, - .reset_page_addr = cdns_reset_page_addr, - .set_bus_conf = cdns_bus_conf, -}; - -/* - * probe and init - */ -static int intel_probe(struct platform_device *pdev) -{ - struct sdw_cdns_stream_config config; - struct sdw_intel *sdw; - int ret; - - sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL); - if (!sdw) - return -ENOMEM; - - sdw->instance = pdev->id; - sdw->res = dev_get_platdata(&pdev->dev); - sdw->cdns.dev = &pdev->dev; - sdw->cdns.registers = sdw->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); - - /* Set property read ops */ - sdw_intel_ops.read_prop = intel_prop_read; - sdw->cdns.bus.ops = &sdw_intel_ops; - - sdw_intel_ops.read_prop = intel_prop_read; - sdw->cdns.bus.ops = &sdw_intel_ops; - - platform_set_drvdata(pdev, sdw); - - ret = sdw_add_bus_master(&sdw->cdns.bus); - if (ret) { - dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); - goto err_master_reg; - } - - /* Initialize shim and controller */ - intel_link_power_up(sdw); - intel_shim_init(sdw); - - ret = sdw_cdns_init(&sdw->cdns); - if (ret) - goto err_init; - - ret = sdw_cdns_enable_interrupt(&sdw->cdns); - - /* Read the PDI config and initialize cadence PDI */ - intel_pdi_init(sdw, &config); - ret = sdw_cdns_pdi_init(&sdw->cdns, config); - if (ret) - goto err_init; - - intel_pdi_ch_update(sdw); - - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, - sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, - &sdw->cdns); - if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", - sdw->res->irq); - goto err_init; - } - - /* Register DAIs */ - ret = intel_register_dai(sdw); - if (ret) { - dev_err(sdw->cdns.dev, "DAI registration failed: %d", ret); - snd_soc_unregister_component(sdw->cdns.dev); - goto err_dai; - } - - return 0; - -err_dai: - free_irq(sdw->res->irq, sdw); -err_init: - sdw_delete_bus_master(&sdw->cdns.bus); -err_master_reg: - return ret; -} - -static int intel_remove(struct platform_device *pdev) -{ - struct sdw_intel *sdw; - - sdw = platform_get_drvdata(pdev); - - free_irq(sdw->res->irq, sdw); - snd_soc_unregister_component(sdw->cdns.dev); - sdw_delete_bus_master(&sdw->cdns.bus); - - return 0; -} - -static struct platform_driver sdw_intel_drv = { - .probe = intel_probe, - .remove = intel_remove, - .driver = { - .name = "int-sdw", - - }, -}; - -module_platform_driver(sdw_intel_drv); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS("platform:int-sdw"); -MODULE_DESCRIPTION("Intel Soundwire Master Driver"); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h deleted file mode 100644 index c1a5bac6212e..000000000000 --- a/drivers/soundwire/intel.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -#ifndef __SDW_INTEL_LOCAL_H -#define __SDW_INTEL_LOCAL_H - -/** - * struct sdw_intel_res - Soundwire link resources - * @registers: Link IO registers base - * @shim: Audio shim pointer - * @alh: ALH (Audio Link Hub) pointer - * @irq: Interrupt line - * @ops: Shim callback ops - * @arg: Shim callback ops argument - * - * This is set as pdata for each link instance. - */ -struct sdw_intel_link_res { - void __iomem *registers; - void __iomem *shim; - void __iomem *alh; - int irq; - const struct sdw_intel_ops *ops; - void *arg; -}; - -#endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c deleted file mode 100644 index 5c8a20d99878..000000000000 --- a/drivers/soundwire/intel_init.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -/* - * SDW Intel Init Routines - * - * Initializes and creates SDW devices based on ACPI and Hardware values - */ - -#include -#include -#include -#include "intel.h" - -#define SDW_MAX_LINKS 4 -#define SDW_SHIM_LCAP 0x0 -#define SDW_SHIM_BASE 0x2C000 -#define SDW_ALH_BASE 0x2C800 -#define SDW_LINK_BASE 0x30000 -#define SDW_LINK_SIZE 0x10000 - -struct sdw_link_data { - struct sdw_intel_link_res res; - struct platform_device *pdev; -}; - -struct sdw_intel_ctx { - int count; - struct sdw_link_data *links; -}; - -static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) -{ - struct sdw_link_data *link = ctx->links; - int i; - - if (!link) - return 0; - - for (i = 0; i < ctx->count; i++) { - if (link->pdev) - platform_device_unregister(link->pdev); - link++; - } - - kfree(ctx->links); - ctx->links = NULL; - - return 0; -} - -static struct sdw_intel_ctx -*sdw_intel_add_controller(struct sdw_intel_res *res) -{ - struct platform_device_info pdevinfo; - struct platform_device *pdev; - struct sdw_link_data *link; - struct sdw_intel_ctx *ctx; - struct acpi_device *adev; - int ret, i; - u8 count; - u32 caps; - - if (acpi_bus_get_device(res->handle, &adev)) - return NULL; - - /* Found controller, find links supported */ - count = 0; - ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), - "mipi-sdw-master-count", &count, 1); - - /* Don't fail on error, continue and use hw value */ - if (ret) { - dev_err(&adev->dev, - "Failed to read mipi-sdw-master-count: %d\n", ret); - count = SDW_MAX_LINKS; - } - - /* Check SNDWLCAP.LCOUNT */ - caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); - - /* Check HW supported vs property value and use min of two */ - count = min_t(u8, caps, count); - - /* Check count is within bounds */ - if (count > SDW_MAX_LINKS) { - dev_err(&adev->dev, "Link count %d exceeds max %d\n", - count, SDW_MAX_LINKS); - return NULL; - } - - dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return NULL; - - ctx->count = count; - ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL); - if (!ctx->links) - goto link_err; - - link = ctx->links; - - /* Create SDW Master devices */ - for (i = 0; i < count; i++) { - - link->res.irq = res->irq; - link->res.registers = res->mmio_base + SDW_LINK_BASE - + (SDW_LINK_SIZE * i); - link->res.shim = res->mmio_base + SDW_SHIM_BASE; - link->res.alh = res->mmio_base + SDW_ALH_BASE; - - link->res.ops = res->ops; - link->res.arg = res->arg; - - memset(&pdevinfo, 0, sizeof(pdevinfo)); - - pdevinfo.parent = res->parent; - pdevinfo.name = "int-sdw"; - pdevinfo.id = i; - pdevinfo.fwnode = acpi_fwnode_handle(adev); - pdevinfo.data = &link->res; - pdevinfo.size_data = sizeof(link->res); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - dev_err(&adev->dev, - "platform device creation failed: %ld\n", - PTR_ERR(pdev)); - goto pdev_err; - } - - link->pdev = pdev; - link++; - } - - return ctx; - -pdev_err: - sdw_intel_cleanup_pdev(ctx); -link_err: - kfree(ctx); - return NULL; -} - -static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, - void *cdata, void **return_value) -{ - struct sdw_intel_res *res = cdata; - struct acpi_device *adev; - - if (acpi_bus_get_device(handle, &adev)) { - pr_err("%s: Couldn't find ACPI handle\n", __func__); - return AE_NOT_FOUND; - } - - res->handle = handle; - return AE_OK; -} - -/** - * sdw_intel_init() - SoundWire Intel init routine - * @parent_handle: ACPI parent handle - * @res: resource data - * - * This scans the namespace and creates SoundWire link controller devices - * based on the info queried. - */ -void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) -{ - acpi_status status; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, - parent_handle, 1, - sdw_intel_acpi_cb, - NULL, res, NULL); - if (ACPI_FAILURE(status)) - return NULL; - - return sdw_intel_add_controller(res); -} -EXPORT_SYMBOL(sdw_intel_init); - -/** - * sdw_intel_exit() - SoundWire Intel exit - * @arg: callback context - * - * Delete the controller instances created and cleanup - */ -void sdw_intel_exit(void *arg) -{ - struct sdw_intel_ctx *ctx = arg; - - sdw_intel_cleanup_pdev(ctx); - kfree(ctx); -} -EXPORT_SYMBOL(sdw_intel_exit); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("Intel Soundwire Init Library"); diff --git a/drivers/soundwire/mipi_disco.c b/drivers/soundwire/mipi_disco.c deleted file mode 100644 index fdeba0c3b589..000000000000 --- a/drivers/soundwire/mipi_disco.c +++ /dev/null @@ -1,401 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -/* - * MIPI Discovery And Configuration (DisCo) Specification for SoundWire - * specifies properties to be implemented for SoundWire Masters and Slaves. - * The DisCo spec doesn't mandate these properties. However, SDW bus cannot - * work without knowing these values. - * - * The helper functions read the Master and Slave properties. Implementers - * of Master or Slave drivers can use any of the below three mechanisms: - * a) Use these APIs here as .read_prop() callback for Master and Slave - * b) Implement own methods and set those as .read_prop(), but invoke - * APIs in this file for generic read and override the values with - * platform specific data - * c) Implement ones own methods which do not use anything provided - * here - */ - -#include -#include -#include -#include -#include "bus.h" - -/** - * sdw_master_read_prop() - Read Master properties - * @bus: SDW bus instance - */ -int sdw_master_read_prop(struct sdw_bus *bus) -{ - struct sdw_master_prop *prop = &bus->prop; - struct fwnode_handle *link; - char name[32]; - int nval, i; - - device_property_read_u32(bus->dev, - "mipi-sdw-sw-interface-revision", &prop->revision); - - /* Find master handle */ - snprintf(name, sizeof(name), - "mipi-sdw-master-%d-subproperties", bus->link_id); - - link = device_get_named_child_node(bus->dev, name); - if (!link) { - dev_err(bus->dev, "Master node %s not found\n", name); - return -EIO; - } - - if (fwnode_property_read_bool(link, - "mipi-sdw-clock-stop-mode0-supported") == true) - prop->clk_stop_mode = SDW_CLK_STOP_MODE0; - - if (fwnode_property_read_bool(link, - "mipi-sdw-clock-stop-mode1-supported") == true) - prop->clk_stop_mode |= SDW_CLK_STOP_MODE1; - - fwnode_property_read_u32(link, - "mipi-sdw-max-clock-frequency", &prop->max_freq); - - nval = fwnode_property_read_u32_array(link, - "mipi-sdw-clock-frequencies-supported", NULL, 0); - if (nval > 0) { - - prop->num_freq = nval; - prop->freq = devm_kcalloc(bus->dev, prop->num_freq, - sizeof(*prop->freq), GFP_KERNEL); - if (!prop->freq) - return -ENOMEM; - - fwnode_property_read_u32_array(link, - "mipi-sdw-clock-frequencies-supported", - prop->freq, prop->num_freq); - } - - /* - * Check the frequencies supported. If FW doesn't provide max - * freq, then populate here by checking values. - */ - if (!prop->max_freq && prop->freq) { - prop->max_freq = prop->freq[0]; - for (i = 1; i < prop->num_freq; i++) { - if (prop->freq[i] > prop->max_freq) - prop->max_freq = prop->freq[i]; - } - } - - nval = fwnode_property_read_u32_array(link, - "mipi-sdw-supported-clock-gears", NULL, 0); - if (nval > 0) { - - prop->num_clk_gears = nval; - prop->clk_gears = devm_kcalloc(bus->dev, prop->num_clk_gears, - sizeof(*prop->clk_gears), GFP_KERNEL); - if (!prop->clk_gears) - return -ENOMEM; - - fwnode_property_read_u32_array(link, - "mipi-sdw-supported-clock-gears", - prop->clk_gears, prop->num_clk_gears); - } - - fwnode_property_read_u32(link, "mipi-sdw-default-frame-rate", - &prop->default_frame_rate); - - fwnode_property_read_u32(link, "mipi-sdw-default-frame-row-size", - &prop->default_row); - - fwnode_property_read_u32(link, "mipi-sdw-default-frame-col-size", - &prop->default_col); - - prop->dynamic_frame = fwnode_property_read_bool(link, - "mipi-sdw-dynamic-frame-shape"); - - fwnode_property_read_u32(link, "mipi-sdw-command-error-threshold", - &prop->err_threshold); - - return 0; -} -EXPORT_SYMBOL(sdw_master_read_prop); - -static int sdw_slave_read_dp0(struct sdw_slave *slave, - struct fwnode_handle *port, struct sdw_dp0_prop *dp0) -{ - int nval; - - fwnode_property_read_u32(port, "mipi-sdw-port-max-wordlength", - &dp0->max_word); - - fwnode_property_read_u32(port, "mipi-sdw-port-min-wordlength", - &dp0->min_word); - - nval = fwnode_property_read_u32_array(port, - "mipi-sdw-port-wordlength-configs", NULL, 0); - if (nval > 0) { - - dp0->num_words = nval; - dp0->words = devm_kcalloc(&slave->dev, - dp0->num_words, sizeof(*dp0->words), - GFP_KERNEL); - if (!dp0->words) - return -ENOMEM; - - fwnode_property_read_u32_array(port, - "mipi-sdw-port-wordlength-configs", - dp0->words, dp0->num_words); - } - - dp0->flow_controlled = fwnode_property_read_bool( - port, "mipi-sdw-bra-flow-controlled"); - - dp0->simple_ch_prep_sm = fwnode_property_read_bool( - port, "mipi-sdw-simplified-channel-prepare-sm"); - - dp0->device_interrupts = fwnode_property_read_bool( - port, "mipi-sdw-imp-def-dp0-interrupts-supported"); - - return 0; -} - -static int sdw_slave_read_dpn(struct sdw_slave *slave, - struct sdw_dpn_prop *dpn, int count, int ports, char *type) -{ - struct fwnode_handle *node; - u32 bit, i = 0; - int nval; - unsigned long addr; - char name[40]; - - addr = ports; - /* valid ports are 1 to 14 so apply mask */ - addr &= GENMASK(14, 1); - - for_each_set_bit(bit, &addr, 32) { - snprintf(name, sizeof(name), - "mipi-sdw-dp-%d-%s-subproperties", bit, type); - - dpn[i].num = bit; - - node = device_get_named_child_node(&slave->dev, name); - if (!node) { - dev_err(&slave->dev, "%s dpN not found\n", name); - return -EIO; - } - - fwnode_property_read_u32(node, "mipi-sdw-port-max-wordlength", - &dpn[i].max_word); - fwnode_property_read_u32(node, "mipi-sdw-port-min-wordlength", - &dpn[i].min_word); - - nval = fwnode_property_read_u32_array(node, - "mipi-sdw-port-wordlength-configs", NULL, 0); - if (nval > 0) { - - dpn[i].num_words = nval; - dpn[i].words = devm_kcalloc(&slave->dev, - dpn[i].num_words, - sizeof(*dpn[i].words), GFP_KERNEL); - if (!dpn[i].words) - return -ENOMEM; - - fwnode_property_read_u32_array(node, - "mipi-sdw-port-wordlength-configs", - dpn[i].words, dpn[i].num_words); - } - - fwnode_property_read_u32(node, "mipi-sdw-data-port-type", - &dpn[i].type); - - fwnode_property_read_u32(node, - "mipi-sdw-max-grouping-supported", - &dpn[i].max_grouping); - - dpn[i].simple_ch_prep_sm = fwnode_property_read_bool(node, - "mipi-sdw-simplified-channelprepare-sm"); - - fwnode_property_read_u32(node, - "mipi-sdw-port-channelprepare-timeout", - &dpn[i].ch_prep_timeout); - - fwnode_property_read_u32(node, - "mipi-sdw-imp-def-dpn-interrupts-supported", - &dpn[i].device_interrupts); - - fwnode_property_read_u32(node, "mipi-sdw-min-channel-number", - &dpn[i].min_ch); - - fwnode_property_read_u32(node, "mipi-sdw-max-channel-number", - &dpn[i].max_ch); - - nval = fwnode_property_read_u32_array(node, - "mipi-sdw-channel-number-list", NULL, 0); - if (nval > 0) { - - dpn[i].num_ch = nval; - dpn[i].ch = devm_kcalloc(&slave->dev, dpn[i].num_ch, - sizeof(*dpn[i].ch), GFP_KERNEL); - if (!dpn[i].ch) - return -ENOMEM; - - fwnode_property_read_u32_array(node, - "mipi-sdw-channel-number-list", - dpn[i].ch, dpn[i].num_ch); - } - - nval = fwnode_property_read_u32_array(node, - "mipi-sdw-channel-combination-list", NULL, 0); - if (nval > 0) { - - dpn[i].num_ch_combinations = nval; - dpn[i].ch_combinations = devm_kcalloc(&slave->dev, - dpn[i].num_ch_combinations, - sizeof(*dpn[i].ch_combinations), - GFP_KERNEL); - if (!dpn[i].ch_combinations) - return -ENOMEM; - - fwnode_property_read_u32_array(node, - "mipi-sdw-channel-combination-list", - dpn[i].ch_combinations, - dpn[i].num_ch_combinations); - } - - fwnode_property_read_u32(node, - "mipi-sdw-modes-supported", &dpn[i].modes); - - fwnode_property_read_u32(node, "mipi-sdw-max-async-buffer", - &dpn[i].max_async_buffer); - - dpn[i].block_pack_mode = fwnode_property_read_bool(node, - "mipi-sdw-block-packing-mode"); - - fwnode_property_read_u32(node, "mipi-sdw-port-encoding-type", - &dpn[i].port_encoding); - - /* TODO: Read audio mode */ - - i++; - } - - return 0; -} - -/** - * sdw_slave_read_prop() - Read Slave properties - * @slave: SDW Slave - */ -int sdw_slave_read_prop(struct sdw_slave *slave) -{ - struct sdw_slave_prop *prop = &slave->prop; - struct device *dev = &slave->dev; - struct fwnode_handle *port; - int num_of_ports, nval, i, dp0 = 0; - - device_property_read_u32(dev, "mipi-sdw-sw-interface-revision", - &prop->mipi_revision); - - prop->wake_capable = device_property_read_bool(dev, - "mipi-sdw-wake-up-unavailable"); - prop->wake_capable = !prop->wake_capable; - - prop->test_mode_capable = device_property_read_bool(dev, - "mipi-sdw-test-mode-supported"); - - prop->clk_stop_mode1 = false; - if (device_property_read_bool(dev, - "mipi-sdw-clock-stop-mode1-supported")) - prop->clk_stop_mode1 = true; - - prop->simple_clk_stop_capable = device_property_read_bool(dev, - "mipi-sdw-simplified-clockstopprepare-sm-supported"); - - device_property_read_u32(dev, "mipi-sdw-clockstopprepare-timeout", - &prop->clk_stop_timeout); - - device_property_read_u32(dev, "mipi-sdw-slave-channelprepare-timeout", - &prop->ch_prep_timeout); - - device_property_read_u32(dev, - "mipi-sdw-clockstopprepare-hard-reset-behavior", - &prop->reset_behave); - - prop->high_PHY_capable = device_property_read_bool(dev, - "mipi-sdw-highPHY-capable"); - - prop->paging_support = device_property_read_bool(dev, - "mipi-sdw-paging-support"); - - prop->bank_delay_support = device_property_read_bool(dev, - "mipi-sdw-bank-delay-support"); - - device_property_read_u32(dev, - "mipi-sdw-port15-read-behavior", &prop->p15_behave); - - device_property_read_u32(dev, "mipi-sdw-master-count", - &prop->master_count); - - device_property_read_u32(dev, "mipi-sdw-source-port-list", - &prop->source_ports); - - device_property_read_u32(dev, "mipi-sdw-sink-port-list", - &prop->sink_ports); - - /* Read dp0 properties */ - port = device_get_named_child_node(dev, "mipi-sdw-dp-0-subproperties"); - if (!port) { - dev_dbg(dev, "DP0 node not found!!\n"); - } else { - - prop->dp0_prop = devm_kzalloc(&slave->dev, - sizeof(*prop->dp0_prop), GFP_KERNEL); - if (!prop->dp0_prop) - return -ENOMEM; - - sdw_slave_read_dp0(slave, port, prop->dp0_prop); - dp0 = 1; - } - - /* - * Based on each DPn port, get source and sink dpn properties. - * Also, some ports can operate as both source or sink. - */ - - /* Allocate memory for set bits in port lists */ - nval = hweight32(prop->source_ports); - prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), GFP_KERNEL); - if (!prop->src_dpn_prop) - return -ENOMEM; - - /* Read dpn properties for source port(s) */ - sdw_slave_read_dpn(slave, prop->src_dpn_prop, nval, - prop->source_ports, "source"); - - nval = hweight32(prop->sink_ports); - prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->sink_dpn_prop), GFP_KERNEL); - if (!prop->sink_dpn_prop) - return -ENOMEM; - - /* Read dpn properties for sink port(s) */ - sdw_slave_read_dpn(slave, prop->sink_dpn_prop, nval, - prop->sink_ports, "sink"); - - /* some ports are bidirectional so check total ports by ORing */ - nval = prop->source_ports | prop->sink_ports; - num_of_ports = hweight32(nval) + dp0; /* add DP0 */ - - /* Allocate port_ready based on num_of_ports */ - slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports, - sizeof(*slave->port_ready), GFP_KERNEL); - if (!slave->port_ready) - return -ENOMEM; - - /* Initialize completion */ - for (i = 0; i < num_of_ports; i++) - init_completion(&slave->port_ready[i]); - - return 0; -} -EXPORT_SYMBOL(sdw_slave_read_prop); diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c deleted file mode 100644 index ac103bd0c176..000000000000 --- a/drivers/soundwire/slave.c +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-17 Intel Corporation. - -#include -#include -#include -#include "bus.h" - -static void sdw_slave_release(struct device *dev) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - - kfree(slave); -} - -static int sdw_slave_add(struct sdw_bus *bus, - struct sdw_slave_id *id, struct fwnode_handle *fwnode) -{ - struct sdw_slave *slave; - int ret; - - slave = kzalloc(sizeof(*slave), GFP_KERNEL); - if (!slave) - return -ENOMEM; - - /* Initialize data structure */ - memcpy(&slave->id, id, sizeof(*id)); - slave->dev.parent = bus->dev; - slave->dev.fwnode = fwnode; - - /* name shall be sdw:link:mfg:part:class:unique */ - dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", - bus->link_id, id->mfg_id, id->part_id, - id->class_id, id->unique_id); - - slave->dev.release = sdw_slave_release; - slave->dev.bus = &sdw_bus_type; - slave->bus = bus; - slave->status = SDW_SLAVE_UNATTACHED; - slave->dev_num = 0; - - mutex_lock(&bus->bus_lock); - list_add_tail(&slave->node, &bus->slaves); - mutex_unlock(&bus->bus_lock); - - ret = device_register(&slave->dev); - if (ret) { - dev_err(bus->dev, "Failed to add slave: ret %d\n", ret); - - /* - * On err, don't free but drop ref as this will be freed - * when release method is invoked. - */ - mutex_lock(&bus->bus_lock); - list_del(&slave->node); - mutex_unlock(&bus->bus_lock); - put_device(&slave->dev); - } - - return ret; -} - -#if IS_ENABLED(CONFIG_ACPI) -/* - * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node - * @bus: SDW bus instance - * - * Scans Master ACPI node for SDW child Slave devices and registers it. - */ -int sdw_acpi_find_slaves(struct sdw_bus *bus) -{ - struct acpi_device *adev, *parent; - - parent = ACPI_COMPANION(bus->dev); - if (!parent) { - dev_err(bus->dev, "Can't find parent for acpi bind\n"); - return -ENODEV; - } - - list_for_each_entry(adev, &parent->children, node) { - unsigned long long addr; - struct sdw_slave_id id; - unsigned int link_id; - acpi_status status; - - status = acpi_evaluate_integer(adev->handle, - METHOD_NAME__ADR, NULL, &addr); - - if (ACPI_FAILURE(status)) { - dev_err(bus->dev, "_ADR resolution failed: %x\n", - status); - return status; - } - - /* Extract link id from ADR, Bit 51 to 48 (included) */ - link_id = (addr >> 48) & GENMASK(3, 0); - - /* Check for link_id match */ - if (link_id != bus->link_id) - continue; - - sdw_extract_slave_id(bus, addr, &id); - - /* - * don't error check for sdw_slave_add as we want to continue - * adding Slaves - */ - sdw_slave_add(bus, &id, acpi_fwnode_handle(adev)); - } - - return 0; -} - -#endif diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c deleted file mode 100644 index 42bc701e2304..000000000000 --- a/drivers/soundwire/stream.c +++ /dev/null @@ -1,1496 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -// Copyright(c) 2015-18 Intel Corporation. - -/* - * stream.c - SoundWire Bus stream operations. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "bus.h" - -/* - * Array of supported rows and columns as per MIPI SoundWire Specification 1.1 - * - * The rows are arranged as per the array index value programmed - * in register. The index 15 has dummy value 0 in order to fill hole. - */ -int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, - 96, 100, 120, 128, 150, 160, 250, 0, - 192, 200, 240, 256, 72, 144, 90, 180}; - -int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; - -static int sdw_find_col_index(int col) -{ - int i; - - for (i = 0; i < SDW_FRAME_COLS; i++) { - if (cols[i] == col) - return i; - } - - pr_warn("Requested column not found, selecting lowest column no: 2\n"); - return 0; -} - -static int sdw_find_row_index(int row) -{ - int i; - - for (i = 0; i < SDW_FRAME_ROWS; i++) { - if (rows[i] == row) - return i; - } - - pr_warn("Requested row not found, selecting lowest row no: 48\n"); - return 0; -} -static int _sdw_program_slave_port_params(struct sdw_bus *bus, - struct sdw_slave *slave, - struct sdw_transport_params *t_params, - enum sdw_dpn_type type) -{ - u32 addr1, addr2, addr3, addr4; - int ret; - u16 wbuf; - - if (bus->params.next_bank) { - addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num); - addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num); - addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num); - addr4 = SDW_DPN_HCTRL_B1(t_params->port_num); - } else { - addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num); - addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num); - addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num); - addr4 = SDW_DPN_HCTRL_B0(t_params->port_num); - } - - /* Program DPN_OffsetCtrl2 registers */ - ret = sdw_write(slave, addr1, t_params->offset2); - if (ret < 0) { - dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed"); - return ret; - } - - /* Program DPN_BlockCtrl3 register */ - ret = sdw_write(slave, addr2, t_params->blk_pkg_mode); - if (ret < 0) { - dev_err(bus->dev, "DPN_BlockCtrl3 register write failed"); - return ret; - } - - /* - * Data ports are FULL, SIMPLE and REDUCED. This function handles - * FULL and REDUCED only and and beyond this point only FULL is - * handled, so bail out if we are not FULL data port type - */ - if (type != SDW_DPN_FULL) - return ret; - - /* Program DPN_SampleCtrl2 register */ - wbuf = (t_params->sample_interval - 1); - wbuf &= SDW_DPN_SAMPLECTRL_HIGH; - wbuf >>= SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH); - - ret = sdw_write(slave, addr3, wbuf); - if (ret < 0) { - dev_err(bus->dev, "DPN_SampleCtrl2 register write failed"); - return ret; - } - - /* Program DPN_HCtrl register */ - wbuf = t_params->hstart; - wbuf <<= SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART); - wbuf |= t_params->hstop; - - ret = sdw_write(slave, addr4, wbuf); - if (ret < 0) - dev_err(bus->dev, "DPN_HCtrl register write failed"); - - return ret; -} - -static int sdw_program_slave_port_params(struct sdw_bus *bus, - struct sdw_slave_runtime *s_rt, - struct sdw_port_runtime *p_rt) -{ - struct sdw_transport_params *t_params = &p_rt->transport_params; - struct sdw_port_params *p_params = &p_rt->port_params; - struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; - u32 addr1, addr2, addr3, addr4, addr5, addr6; - struct sdw_dpn_prop *dpn_prop; - int ret; - u8 wbuf; - - dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, - s_rt->direction, - t_params->port_num); - if (!dpn_prop) - return -EINVAL; - - addr1 = SDW_DPN_PORTCTRL(t_params->port_num); - addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num); - - if (bus->params.next_bank) { - addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num); - addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num); - addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num); - addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num); - - } else { - addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num); - addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num); - addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num); - addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num); - } - - /* Program DPN_PortCtrl register */ - wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE); - wbuf |= p_params->flow_mode; - - ret = sdw_update(s_rt->slave, addr1, 0xF, wbuf); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_PortCtrl register write failed for port %d", - t_params->port_num); - return ret; - } - - /* Program DPN_BlockCtrl1 register */ - ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1)); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_BlockCtrl1 register write failed for port %d", - t_params->port_num); - return ret; - } - - /* Program DPN_SampleCtrl1 register */ - wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW; - ret = sdw_write(s_rt->slave, addr3, wbuf); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_SampleCtrl1 register write failed for port %d", - t_params->port_num); - return ret; - } - - /* Program DPN_OffsetCtrl1 registers */ - ret = sdw_write(s_rt->slave, addr4, t_params->offset1); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_OffsetCtrl1 register write failed for port %d", - t_params->port_num); - return ret; - } - - /* Program DPN_BlockCtrl2 register*/ - if (t_params->blk_grp_ctrl_valid) { - ret = sdw_write(s_rt->slave, addr5, t_params->blk_grp_ctrl); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_BlockCtrl2 reg write failed for port %d", - t_params->port_num); - return ret; - } - } - - /* program DPN_LaneCtrl register */ - if (slave_prop->lane_control_support) { - ret = sdw_write(s_rt->slave, addr6, t_params->lane_ctrl); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "DPN_LaneCtrl register write failed for port %d", - t_params->port_num); - return ret; - } - } - - if (dpn_prop->type != SDW_DPN_SIMPLE) { - ret = _sdw_program_slave_port_params(bus, s_rt->slave, - t_params, dpn_prop->type); - if (ret < 0) - dev_err(&s_rt->slave->dev, - "Transport reg write failed for port: %d", - t_params->port_num); - } - - return ret; -} - -static int sdw_program_master_port_params(struct sdw_bus *bus, - struct sdw_port_runtime *p_rt) -{ - int ret; - - /* - * we need to set transport and port parameters for the port. - * Transport parameters refers to the smaple interval, offsets and - * hstart/stop etc of the data. Port parameters refers to word - * length, flow mode etc of the port - */ - ret = bus->port_ops->dpn_set_port_transport_params(bus, - &p_rt->transport_params, - bus->params.next_bank); - if (ret < 0) - return ret; - - return bus->port_ops->dpn_set_port_params(bus, - &p_rt->port_params, - bus->params.next_bank); -} - -/** - * sdw_program_port_params() - Programs transport parameters of Master(s) - * and Slave(s) - * - * @m_rt: Master stream runtime - */ -static int sdw_program_port_params(struct sdw_master_runtime *m_rt) -{ - struct sdw_slave_runtime *s_rt = NULL; - struct sdw_bus *bus = m_rt->bus; - struct sdw_port_runtime *p_rt; - int ret = 0; - - /* Program transport & port parameters for Slave(s) */ - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - list_for_each_entry(p_rt, &s_rt->port_list, port_node) { - ret = sdw_program_slave_port_params(bus, s_rt, p_rt); - if (ret < 0) - return ret; - } - } - - /* Program transport & port parameters for Master(s) */ - list_for_each_entry(p_rt, &m_rt->port_list, port_node) { - ret = sdw_program_master_port_params(bus, p_rt); - if (ret < 0) - return ret; - } - - return 0; -} - -/** - * sdw_enable_disable_slave_ports: Enable/disable slave data port - * - * @bus: bus instance - * @s_rt: slave runtime - * @p_rt: port runtime - * @en: enable or disable operation - * - * This function only sets the enable/disable bits in the relevant bank, the - * actual enable/disable is done with a bank switch - */ -static int sdw_enable_disable_slave_ports(struct sdw_bus *bus, - struct sdw_slave_runtime *s_rt, - struct sdw_port_runtime *p_rt, bool en) -{ - struct sdw_transport_params *t_params = &p_rt->transport_params; - u32 addr; - int ret; - - if (bus->params.next_bank) - addr = SDW_DPN_CHANNELEN_B1(p_rt->num); - else - addr = SDW_DPN_CHANNELEN_B0(p_rt->num); - - /* - * Since bus doesn't support sharing a port across two streams, - * it is safe to reset this register - */ - if (en) - ret = sdw_update(s_rt->slave, addr, 0xFF, p_rt->ch_mask); - else - ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0); - - if (ret < 0) - dev_err(&s_rt->slave->dev, - "Slave chn_en reg write failed:%d port:%d", - ret, t_params->port_num); - - return ret; -} - -static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt, - struct sdw_port_runtime *p_rt, bool en) -{ - struct sdw_transport_params *t_params = &p_rt->transport_params; - struct sdw_bus *bus = m_rt->bus; - struct sdw_enable_ch enable_ch; - int ret = 0; - - enable_ch.port_num = p_rt->num; - enable_ch.ch_mask = p_rt->ch_mask; - enable_ch.enable = en; - - /* Perform Master port channel(s) enable/disable */ - if (bus->port_ops->dpn_port_enable_ch) { - ret = bus->port_ops->dpn_port_enable_ch(bus, - &enable_ch, bus->params.next_bank); - if (ret < 0) { - dev_err(bus->dev, - "Master chn_en write failed:%d port:%d", - ret, t_params->port_num); - return ret; - } - } else { - dev_err(bus->dev, - "dpn_port_enable_ch not supported, %s failed\n", - en ? "enable" : "disable"); - return -EINVAL; - } - - return 0; -} - -/** - * sdw_enable_disable_ports() - Enable/disable port(s) for Master and - * Slave(s) - * - * @m_rt: Master stream runtime - * @en: mode (enable/disable) - */ -static int sdw_enable_disable_ports(struct sdw_master_runtime *m_rt, bool en) -{ - struct sdw_port_runtime *s_port, *m_port; - struct sdw_slave_runtime *s_rt = NULL; - int ret = 0; - - /* Enable/Disable Slave port(s) */ - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - list_for_each_entry(s_port, &s_rt->port_list, port_node) { - ret = sdw_enable_disable_slave_ports(m_rt->bus, s_rt, - s_port, en); - if (ret < 0) - return ret; - } - } - - /* Enable/Disable Master port(s) */ - list_for_each_entry(m_port, &m_rt->port_list, port_node) { - ret = sdw_enable_disable_master_ports(m_rt, m_port, en); - if (ret < 0) - return ret; - } - - return 0; -} - -static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt, - struct sdw_prepare_ch prep_ch, enum sdw_port_prep_ops cmd) -{ - const struct sdw_slave_ops *ops = s_rt->slave->ops; - int ret; - - if (ops->port_prep) { - ret = ops->port_prep(s_rt->slave, &prep_ch, cmd); - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "Slave Port Prep cmd %d failed: %d", cmd, ret); - return ret; - } - } - - return 0; -} - -static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, - struct sdw_slave_runtime *s_rt, - struct sdw_port_runtime *p_rt, bool prep) -{ - struct completion *port_ready = NULL; - struct sdw_dpn_prop *dpn_prop; - struct sdw_prepare_ch prep_ch; - unsigned int time_left; - bool intr = false; - int ret = 0, val; - u32 addr; - - prep_ch.num = p_rt->num; - prep_ch.ch_mask = p_rt->ch_mask; - - dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, - s_rt->direction, - prep_ch.num); - if (!dpn_prop) { - dev_err(bus->dev, - "Slave Port:%d properties not found", prep_ch.num); - return -EINVAL; - } - - prep_ch.prepare = prep; - - prep_ch.bank = bus->params.next_bank; - - if (dpn_prop->device_interrupts || !dpn_prop->simple_ch_prep_sm) - intr = true; - - /* - * Enable interrupt before Port prepare. - * For Port de-prepare, it is assumed that port - * was prepared earlier - */ - if (prep && intr) { - ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, - dpn_prop->device_interrupts); - if (ret < 0) - return ret; - } - - /* Inform slave about the impending port prepare */ - sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_PRE_PREP); - - /* Prepare Slave port implementing CP_SM */ - if (!dpn_prop->simple_ch_prep_sm) { - addr = SDW_DPN_PREPARECTRL(p_rt->num); - - if (prep) - ret = sdw_update(s_rt->slave, addr, - 0xFF, p_rt->ch_mask); - else - ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0); - - if (ret < 0) { - dev_err(&s_rt->slave->dev, - "Slave prep_ctrl reg write failed"); - return ret; - } - - /* Wait for completion on port ready */ - port_ready = &s_rt->slave->port_ready[prep_ch.num]; - time_left = wait_for_completion_timeout(port_ready, - msecs_to_jiffies(dpn_prop->ch_prep_timeout)); - - val = sdw_read(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); - val &= p_rt->ch_mask; - if (!time_left || val) { - dev_err(&s_rt->slave->dev, - "Chn prep failed for port:%d", prep_ch.num); - return -ETIMEDOUT; - } - } - - /* Inform slaves about ports prepared */ - sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP); - - /* Disable interrupt after Port de-prepare */ - if (!prep && intr) - ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, - dpn_prop->device_interrupts); - - return ret; -} - -static int sdw_prep_deprep_master_ports(struct sdw_master_runtime *m_rt, - struct sdw_port_runtime *p_rt, bool prep) -{ - struct sdw_transport_params *t_params = &p_rt->transport_params; - struct sdw_bus *bus = m_rt->bus; - const struct sdw_master_port_ops *ops = bus->port_ops; - struct sdw_prepare_ch prep_ch; - int ret = 0; - - prep_ch.num = p_rt->num; - prep_ch.ch_mask = p_rt->ch_mask; - prep_ch.prepare = prep; /* Prepare/De-prepare */ - prep_ch.bank = bus->params.next_bank; - - /* Pre-prepare/Pre-deprepare port(s) */ - if (ops->dpn_port_prep) { - ret = ops->dpn_port_prep(bus, &prep_ch); - if (ret < 0) { - dev_err(bus->dev, "Port prepare failed for port:%d", - t_params->port_num); - return ret; - } - } - - return ret; -} - -/** - * sdw_prep_deprep_ports() - Prepare/De-prepare port(s) for Master(s) and - * Slave(s) - * - * @m_rt: Master runtime handle - * @prep: Prepare or De-prepare - */ -static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep) -{ - struct sdw_slave_runtime *s_rt = NULL; - struct sdw_port_runtime *p_rt; - int ret = 0; - - /* Prepare/De-prepare Slave port(s) */ - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - list_for_each_entry(p_rt, &s_rt->port_list, port_node) { - ret = sdw_prep_deprep_slave_ports(m_rt->bus, s_rt, - p_rt, prep); - if (ret < 0) - return ret; - } - } - - /* Prepare/De-prepare Master port(s) */ - list_for_each_entry(p_rt, &m_rt->port_list, port_node) { - ret = sdw_prep_deprep_master_ports(m_rt, p_rt, prep); - if (ret < 0) - return ret; - } - - return ret; -} - -/** - * sdw_notify_config() - Notify bus configuration - * - * @m_rt: Master runtime handle - * - * This function notifies the Master(s) and Slave(s) of the - * new bus configuration. - */ -static int sdw_notify_config(struct sdw_master_runtime *m_rt) -{ - struct sdw_slave_runtime *s_rt; - struct sdw_bus *bus = m_rt->bus; - struct sdw_slave *slave; - int ret = 0; - - if (bus->ops->set_bus_conf) { - ret = bus->ops->set_bus_conf(bus, &bus->params); - if (ret < 0) - return ret; - } - - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - slave = s_rt->slave; - - if (slave->ops->bus_config) { - ret = slave->ops->bus_config(slave, &bus->params); - if (ret < 0) - dev_err(bus->dev, "Notify Slave: %d failed", - slave->dev_num); - return ret; - } - } - - return ret; -} - -/** - * sdw_program_params() - Program transport and port parameters for Master(s) - * and Slave(s) - * - * @bus: SDW bus instance - */ -static int sdw_program_params(struct sdw_bus *bus) -{ - struct sdw_master_runtime *m_rt = NULL; - int ret = 0; - - list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { - ret = sdw_program_port_params(m_rt); - if (ret < 0) { - dev_err(bus->dev, - "Program transport params failed: %d", ret); - return ret; - } - - ret = sdw_notify_config(m_rt); - if (ret < 0) { - dev_err(bus->dev, "Notify bus config failed: %d", ret); - return ret; - } - - /* Enable port(s) on alternate bank for all active streams */ - if (m_rt->stream->state != SDW_STREAM_ENABLED) - continue; - - ret = sdw_enable_disable_ports(m_rt, true); - if (ret < 0) { - dev_err(bus->dev, "Enable channel failed: %d", ret); - return ret; - } - } - - return ret; -} - -static int sdw_bank_switch(struct sdw_bus *bus) -{ - int col_index, row_index; - struct sdw_msg *wr_msg; - u8 *wbuf = NULL; - int ret = 0; - u16 addr; - - wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL); - if (!wr_msg) - return -ENOMEM; - - wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); - if (!wbuf) { - ret = -ENOMEM; - goto error_1; - } - - /* Get row and column index to program register */ - col_index = sdw_find_col_index(bus->params.col); - row_index = sdw_find_row_index(bus->params.row); - wbuf[0] = col_index | (row_index << 3); - - if (bus->params.next_bank) - addr = SDW_SCP_FRAMECTRL_B1; - else - addr = SDW_SCP_FRAMECTRL_B0; - - sdw_fill_msg(wr_msg, NULL, addr, 1, SDW_BROADCAST_DEV_NUM, - SDW_MSG_FLAG_WRITE, wbuf); - wr_msg->ssp_sync = true; - - ret = sdw_transfer(bus, wr_msg); - if (ret < 0) { - dev_err(bus->dev, "Slave frame_ctrl reg write failed"); - goto error; - } - - kfree(wr_msg); - kfree(wbuf); - bus->defer_msg.msg = NULL; - bus->params.curr_bank = !bus->params.curr_bank; - bus->params.next_bank = !bus->params.next_bank; - - return 0; - -error: - kfree(wbuf); -error_1: - kfree(wr_msg); - return ret; -} - -static int do_bank_switch(struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = stream->m_rt; - const struct sdw_master_ops *ops; - struct sdw_bus *bus = m_rt->bus; - int ret = 0; - - ops = bus->ops; - - /* Pre-bank switch */ - if (ops->pre_bank_switch) { - ret = ops->pre_bank_switch(bus); - if (ret < 0) { - dev_err(bus->dev, "Pre bank switch op failed: %d", ret); - return ret; - } - } - - /* Bank switch */ - ret = sdw_bank_switch(bus); - if (ret < 0) { - dev_err(bus->dev, "Bank switch failed: %d", ret); - return ret; - } - - /* Post-bank switch */ - if (ops->post_bank_switch) { - ret = ops->post_bank_switch(bus); - if (ret < 0) { - dev_err(bus->dev, - "Post bank switch op failed: %d", ret); - } - } - - return ret; -} - -/** - * sdw_release_stream() - Free the assigned stream runtime - * - * @stream: SoundWire stream runtime - * - * sdw_release_stream should be called only once per stream - */ -void sdw_release_stream(struct sdw_stream_runtime *stream) -{ - kfree(stream); -} -EXPORT_SYMBOL(sdw_release_stream); - -/** - * sdw_alloc_stream() - Allocate and return stream runtime - * - * @stream_name: SoundWire stream name - * - * Allocates a SoundWire stream runtime instance. - * sdw_alloc_stream should be called only once per stream. Typically - * invoked from ALSA/ASoC machine/platform driver. - */ -struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name) -{ - struct sdw_stream_runtime *stream; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) - return NULL; - - stream->name = stream_name; - stream->state = SDW_STREAM_ALLOCATED; - - return stream; -} -EXPORT_SYMBOL(sdw_alloc_stream); - -/** - * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle - * - * @bus: SDW bus instance - * @stream_config: Stream configuration - * @stream: Stream runtime handle. - * - * This function is to be called with bus_lock held. - */ -static struct sdw_master_runtime -*sdw_alloc_master_rt(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, - struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt; - - m_rt = stream->m_rt; - - /* - * check if Master is already allocated (as a result of Slave adding - * it first), if so skip allocation and go to configure - */ - if (m_rt) - goto stream_config; - - m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); - if (!m_rt) - return NULL; - - /* Initialization of Master runtime handle */ - INIT_LIST_HEAD(&m_rt->port_list); - INIT_LIST_HEAD(&m_rt->slave_rt_list); - stream->m_rt = m_rt; - - list_add_tail(&m_rt->bus_node, &bus->m_rt_list); - -stream_config: - m_rt->ch_count = stream_config->ch_count; - m_rt->bus = bus; - m_rt->stream = stream; - m_rt->direction = stream_config->direction; - - return m_rt; -} - -/** - * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. - * - * @slave: Slave handle - * @stream_config: Stream configuration - * @stream: Stream runtime handle - * - * This function is to be called with bus_lock held. - */ -static struct sdw_slave_runtime -*sdw_alloc_slave_rt(struct sdw_slave *slave, - struct sdw_stream_config *stream_config, - struct sdw_stream_runtime *stream) -{ - struct sdw_slave_runtime *s_rt = NULL; - - s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL); - if (!s_rt) - return NULL; - - INIT_LIST_HEAD(&s_rt->port_list); - s_rt->ch_count = stream_config->ch_count; - s_rt->direction = stream_config->direction; - s_rt->slave = slave; - - return s_rt; -} - -static void sdw_master_port_release(struct sdw_bus *bus, - struct sdw_master_runtime *m_rt) -{ - struct sdw_port_runtime *p_rt, *_p_rt; - - list_for_each_entry_safe(p_rt, _p_rt, - &m_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); - } -} - -static void sdw_slave_port_release(struct sdw_bus *bus, - struct sdw_slave *slave, - struct sdw_stream_runtime *stream) -{ - struct sdw_port_runtime *p_rt, *_p_rt; - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_slave_runtime *s_rt; - - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - if (s_rt->slave != slave) - continue; - - list_for_each_entry_safe(p_rt, _p_rt, - &s_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); - } - } -} - -/** - * sdw_release_slave_stream() - Free Slave(s) runtime handle - * - * @slave: Slave handle. - * @stream: Stream runtime handle. - * - * This function is to be called with bus_lock held. - */ -static void sdw_release_slave_stream(struct sdw_slave *slave, - struct sdw_stream_runtime *stream) -{ - struct sdw_slave_runtime *s_rt, *_s_rt; - struct sdw_master_runtime *m_rt = stream->m_rt; - - /* Retrieve Slave runtime handle */ - list_for_each_entry_safe(s_rt, _s_rt, - &m_rt->slave_rt_list, m_rt_node) { - - if (s_rt->slave == slave) { - list_del(&s_rt->m_rt_node); - kfree(s_rt); - return; - } - } -} - -/** - * sdw_release_master_stream() - Free Master runtime handle - * - * @stream: Stream runtime handle. - * - * This function is to be called with bus_lock held - * It frees the Master runtime handle and associated Slave(s) runtime - * handle. If this is called first then sdw_release_slave_stream() will have - * no effect as Slave(s) runtime handle would already be freed up. - */ -static void sdw_release_master_stream(struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_slave_runtime *s_rt, *_s_rt; - - list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) { - sdw_slave_port_release(s_rt->slave->bus, s_rt->slave, stream); - sdw_release_slave_stream(s_rt->slave, stream); - } - - list_del(&m_rt->bus_node); -} - -/** - * sdw_stream_remove_master() - Remove master from sdw_stream - * - * @bus: SDW Bus instance - * @stream: SoundWire stream - * - * This removes and frees port_rt and master_rt from a stream - */ -int sdw_stream_remove_master(struct sdw_bus *bus, - struct sdw_stream_runtime *stream) -{ - mutex_lock(&bus->bus_lock); - - sdw_release_master_stream(stream); - sdw_master_port_release(bus, stream->m_rt); - stream->state = SDW_STREAM_RELEASED; - kfree(stream->m_rt); - stream->m_rt = NULL; - - mutex_unlock(&bus->bus_lock); - - return 0; -} -EXPORT_SYMBOL(sdw_stream_remove_master); - -/** - * sdw_stream_remove_slave() - Remove slave from sdw_stream - * - * @slave: SDW Slave instance - * @stream: SoundWire stream - * - * This removes and frees port_rt and slave_rt from a stream - */ -int sdw_stream_remove_slave(struct sdw_slave *slave, - struct sdw_stream_runtime *stream) -{ - mutex_lock(&slave->bus->bus_lock); - - sdw_slave_port_release(slave->bus, slave, stream); - sdw_release_slave_stream(slave, stream); - - mutex_unlock(&slave->bus->bus_lock); - - return 0; -} -EXPORT_SYMBOL(sdw_stream_remove_slave); - -/** - * sdw_config_stream() - Configure the allocated stream - * - * @dev: SDW device - * @stream: SoundWire stream - * @stream_config: Stream configuration for audio stream - * @is_slave: is API called from Slave or Master - * - * This function is to be called with bus_lock held. - */ -static int sdw_config_stream(struct device *dev, - struct sdw_stream_runtime *stream, - struct sdw_stream_config *stream_config, bool is_slave) -{ - /* - * Update the stream rate, channel and bps based on data - * source. For more than one data source (multilink), - * match the rate, bps, stream type and increment number of channels. - * - * If rate/bps is zero, it means the values are not set, so skip - * comparison and allow the value to be set and stored in stream - */ - if (stream->params.rate && - stream->params.rate != stream_config->frame_rate) { - dev_err(dev, "rate not matching, stream:%s", stream->name); - return -EINVAL; - } - - if (stream->params.bps && - stream->params.bps != stream_config->bps) { - dev_err(dev, "bps not matching, stream:%s", stream->name); - return -EINVAL; - } - - stream->type = stream_config->type; - stream->params.rate = stream_config->frame_rate; - stream->params.bps = stream_config->bps; - - /* TODO: Update this check during Device-device support */ - if (is_slave) - stream->params.ch_count += stream_config->ch_count; - - return 0; -} - -static int sdw_is_valid_port_range(struct device *dev, - struct sdw_port_runtime *p_rt) -{ - if (!SDW_VALID_PORT_RANGE(p_rt->num)) { - dev_err(dev, - "SoundWire: Invalid port number :%d", p_rt->num); - return -EINVAL; - } - - return 0; -} - -static struct sdw_port_runtime *sdw_port_alloc(struct device *dev, - struct sdw_port_config *port_config, - int port_index) -{ - struct sdw_port_runtime *p_rt; - - p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); - if (!p_rt) - return NULL; - - p_rt->ch_mask = port_config[port_index].ch_mask; - p_rt->num = port_config[port_index].num; - - return p_rt; -} - -static int sdw_master_port_config(struct sdw_bus *bus, - struct sdw_master_runtime *m_rt, - struct sdw_port_config *port_config, - unsigned int num_ports) -{ - struct sdw_port_runtime *p_rt; - int i; - - /* Iterate for number of ports to perform initialization */ - for (i = 0; i < num_ports; i++) { - p_rt = sdw_port_alloc(bus->dev, port_config, i); - if (!p_rt) - return -ENOMEM; - - /* - * TODO: Check port capabilities for requested - * configuration (audio mode support) - */ - - list_add_tail(&p_rt->port_node, &m_rt->port_list); - } - - return 0; -} - -static int sdw_slave_port_config(struct sdw_slave *slave, - struct sdw_slave_runtime *s_rt, - struct sdw_port_config *port_config, - unsigned int num_config) -{ - struct sdw_port_runtime *p_rt; - int i, ret; - - /* Iterate for number of ports to perform initialization */ - for (i = 0; i < num_config; i++) { - p_rt = sdw_port_alloc(&slave->dev, port_config, i); - if (!p_rt) - return -ENOMEM; - - /* - * TODO: Check valid port range as defined by DisCo/ - * slave - */ - ret = sdw_is_valid_port_range(&slave->dev, p_rt); - if (ret < 0) { - kfree(p_rt); - return ret; - } - - /* - * TODO: Check port capabilities for requested - * configuration (audio mode support) - */ - - list_add_tail(&p_rt->port_node, &s_rt->port_list); - } - - return 0; -} - -/** - * sdw_stream_add_master() - Allocate and add master runtime to a stream - * - * @bus: SDW Bus instance - * @stream_config: Stream configuration for audio stream - * @port_config: Port configuration for audio stream - * @num_ports: Number of ports - * @stream: SoundWire stream - */ -int sdw_stream_add_master(struct sdw_bus *bus, - struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = NULL; - int ret; - - mutex_lock(&bus->bus_lock); - - m_rt = sdw_alloc_master_rt(bus, stream_config, stream); - if (!m_rt) { - dev_err(bus->dev, - "Master runtime config failed for stream:%s", - stream->name); - ret = -ENOMEM; - goto unlock; - } - - ret = sdw_config_stream(bus->dev, stream, stream_config, false); - if (ret) - goto stream_error; - - ret = sdw_master_port_config(bus, m_rt, port_config, num_ports); - if (ret) - goto stream_error; - - goto unlock; - -stream_error: - sdw_release_master_stream(stream); -unlock: - mutex_unlock(&bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_stream_add_master); - -/** - * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream - * - * @slave: SDW Slave instance - * @stream_config: Stream configuration for audio stream - * @stream: SoundWire stream - * @port_config: Port configuration for audio stream - * @num_ports: Number of ports - * - * It is expected that Slave is added before adding Master - * to the Stream. - * - */ -int sdw_stream_add_slave(struct sdw_slave *slave, - struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, - unsigned int num_ports, - struct sdw_stream_runtime *stream) -{ - struct sdw_slave_runtime *s_rt; - struct sdw_master_runtime *m_rt; - int ret; - - mutex_lock(&slave->bus->bus_lock); - - /* - * If this API is invoked by Slave first then m_rt is not valid. - * So, allocate m_rt and add Slave to it. - */ - m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream); - if (!m_rt) { - dev_err(&slave->dev, - "alloc master runtime failed for stream:%s", - stream->name); - ret = -ENOMEM; - goto error; - } - - s_rt = sdw_alloc_slave_rt(slave, stream_config, stream); - if (!s_rt) { - dev_err(&slave->dev, - "Slave runtime config failed for stream:%s", - stream->name); - ret = -ENOMEM; - goto stream_error; - } - - ret = sdw_config_stream(&slave->dev, stream, stream_config, true); - if (ret) { - /* - * sdw_release_master_stream will release s_rt in slave_rt_list in - * stream_error case, but s_rt is only added to slave_rt_list - * when sdw_config_stream is successful, so free s_rt explicitly - * when sdw_config_stream is failed. - */ - kfree(s_rt); - goto stream_error; - } - - list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); - - ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports); - if (ret) - goto stream_error; - - /* - * Change stream state to CONFIGURED on first Slave add. - * Bus is not aware of number of Slave(s) in a stream at this - * point so cannot depend on all Slave(s) to be added in order to - * change stream state to CONFIGURED. - */ - stream->state = SDW_STREAM_CONFIGURED; - goto error; - -stream_error: - /* - * we hit error so cleanup the stream, release all Slave(s) and - * Master runtime - */ - sdw_release_master_stream(stream); -error: - mutex_unlock(&slave->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_stream_add_slave); - -/** - * sdw_get_slave_dpn_prop() - Get Slave port capabilities - * - * @slave: Slave handle - * @direction: Data direction. - * @port_num: Port number - */ -struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, - enum sdw_data_direction direction, - unsigned int port_num) -{ - struct sdw_dpn_prop *dpn_prop; - u8 num_ports; - int i; - - if (direction == SDW_DATA_DIR_TX) { - num_ports = hweight32(slave->prop.source_ports); - dpn_prop = slave->prop.src_dpn_prop; - } else { - num_ports = hweight32(slave->prop.sink_ports); - dpn_prop = slave->prop.sink_dpn_prop; - } - - for (i = 0; i < num_ports; i++) { - if (dpn_prop[i].num == port_num) - return &dpn_prop[i]; - } - - return NULL; -} - -static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; - struct sdw_master_prop *prop = NULL; - struct sdw_bus_params params; - int ret; - - prop = &bus->prop; - memcpy(¶ms, &bus->params, sizeof(params)); - - /* TODO: Support Asynchronous mode */ - if ((prop->max_freq % stream->params.rate) != 0) { - dev_err(bus->dev, "Async mode not supported"); - return -EINVAL; - } - - /* Increment cumulative bus bandwidth */ - /* TODO: Update this during Device-Device support */ - bus->params.bandwidth += m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; - - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - goto restore_params; - } - - ret = do_bank_switch(stream); - if (ret < 0) { - dev_err(bus->dev, "Bank switch failed: %d", ret); - goto restore_params; - } - - /* Prepare port(s) on the new clock configuration */ - ret = sdw_prep_deprep_ports(m_rt, true); - if (ret < 0) { - dev_err(bus->dev, "Prepare port(s) failed ret = %d", - ret); - return ret; - } - - stream->state = SDW_STREAM_PREPARED; - - return ret; - -restore_params: - memcpy(&bus->params, ¶ms, sizeof(params)); - return ret; -} - -/** - * sdw_prepare_stream() - Prepare SoundWire stream - * - * @stream: Soundwire stream - * - * Documentation/driver-api/soundwire/stream.rst explains this API in detail - */ -int sdw_prepare_stream(struct sdw_stream_runtime *stream) -{ - int ret = 0; - - if (!stream) { - pr_err("SoundWire: Handle not found for stream"); - return -EINVAL; - } - - mutex_lock(&stream->m_rt->bus->bus_lock); - - ret = _sdw_prepare_stream(stream); - if (ret < 0) - pr_err("Prepare for stream:%s failed: %d", stream->name, ret); - - mutex_unlock(&stream->m_rt->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_prepare_stream); - -static int _sdw_enable_stream(struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; - int ret; - - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - return ret; - } - - /* Enable port(s) */ - ret = sdw_enable_disable_ports(m_rt, true); - if (ret < 0) { - dev_err(bus->dev, "Enable port(s) failed ret: %d", ret); - return ret; - } - - ret = do_bank_switch(stream); - if (ret < 0) { - dev_err(bus->dev, "Bank switch failed: %d", ret); - return ret; - } - - stream->state = SDW_STREAM_ENABLED; - return 0; -} - -/** - * sdw_enable_stream() - Enable SoundWire stream - * - * @stream: Soundwire stream - * - * Documentation/driver-api/soundwire/stream.rst explains this API in detail - */ -int sdw_enable_stream(struct sdw_stream_runtime *stream) -{ - int ret = 0; - - if (!stream) { - pr_err("SoundWire: Handle not found for stream"); - return -EINVAL; - } - - mutex_lock(&stream->m_rt->bus->bus_lock); - - ret = _sdw_enable_stream(stream); - if (ret < 0) - pr_err("Enable for stream:%s failed: %d", stream->name, ret); - - mutex_unlock(&stream->m_rt->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_enable_stream); - -static int _sdw_disable_stream(struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; - int ret; - - /* Disable port(s) */ - ret = sdw_enable_disable_ports(m_rt, false); - if (ret < 0) { - dev_err(bus->dev, "Disable port(s) failed: %d", ret); - return ret; - } - - stream->state = SDW_STREAM_DISABLED; - - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - return ret; - } - - return do_bank_switch(stream); -} - -/** - * sdw_disable_stream() - Disable SoundWire stream - * - * @stream: Soundwire stream - * - * Documentation/driver-api/soundwire/stream.rst explains this API in detail - */ -int sdw_disable_stream(struct sdw_stream_runtime *stream) -{ - int ret = 0; - - if (!stream) { - pr_err("SoundWire: Handle not found for stream"); - return -EINVAL; - } - - mutex_lock(&stream->m_rt->bus->bus_lock); - - ret = _sdw_disable_stream(stream); - if (ret < 0) - pr_err("Disable for stream:%s failed: %d", stream->name, ret); - - mutex_unlock(&stream->m_rt->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_disable_stream); - -static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) -{ - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; - int ret = 0; - - /* De-prepare port(s) */ - ret = sdw_prep_deprep_ports(m_rt, false); - if (ret < 0) { - dev_err(bus->dev, "De-prepare port(s) failed: %d", ret); - return ret; - } - - stream->state = SDW_STREAM_DEPREPARED; - - /* TODO: Update this during Device-Device support */ - bus->params.bandwidth -= m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; - - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - return ret; - } - - return do_bank_switch(stream); -} - -/** - * sdw_deprepare_stream() - Deprepare SoundWire stream - * - * @stream: Soundwire stream - * - * Documentation/driver-api/soundwire/stream.rst explains this API in detail - */ -int sdw_deprepare_stream(struct sdw_stream_runtime *stream) -{ - int ret = 0; - - if (!stream) { - pr_err("SoundWire: Handle not found for stream"); - return -EINVAL; - } - - mutex_lock(&stream->m_rt->bus->bus_lock); - - ret = _sdw_deprepare_stream(stream); - if (ret < 0) - pr_err("De-prepare for stream:%d failed: %d", ret, ret); - - mutex_unlock(&stream->m_rt->bus->bus_lock); - return ret; -} -EXPORT_SYMBOL(sdw_deprepare_stream);