drivers: soundwire: nuke it!

This commit is contained in:
spakkkk 2020-10-19 16:09:41 +00:00
parent e82f8bdea0
commit dac2787892
15 changed files with 0 additions and 5931 deletions

View File

@ -159,8 +159,6 @@ source "drivers/remoteproc/Kconfig"
source "drivers/rpmsg/Kconfig"
source "drivers/soundwire/Kconfig"
source "drivers/soc/Kconfig"
source "drivers/devfreq/Kconfig"

View File

@ -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/

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -1,188 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2015-17 Intel Corporation.
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/pm_domain.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
/**
* 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:m<mfg_id>p<part_id> */
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");

File diff suppressed because it is too large Load Diff

View File

@ -1,199 +0,0 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2015-17 Intel Corporation.
#include <sound/soc.h>
#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 */

View File

@ -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 <linux/acpi.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
#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");

View File

@ -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 */

View File

@ -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 <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/soundwire/sdw_intel.h>
#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");

View File

@ -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 <linux/device.h>
#include <linux/property.h>
#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#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);

View File

@ -1,114 +0,0 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2015-17 Intel Corporation.
#include <linux/acpi.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#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

File diff suppressed because it is too large Load Diff