diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-11-09 13:18:25 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-11-09 13:18:25 +0100 |
commit | a61ead03a6f2768d501be08d7f5efd7fb3e71555 (patch) | |
tree | f8c43529d7466f21cdaea67c088e98b7afa86648 | |
parent | bce92136c28f3ae9ddbbac0ee7c0df2eb0ae0bef (diff) | |
parent | 2e8c4ad1f04413a4a67ef10746a7566007d2ed55 (diff) |
Merge tag 'soundwire-5.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next
Vinod writes:
soundwire updates for v5.5-rc1
This round we have bunch of core and Intel driver updates spearheaded
by Pierre
Details
- Update unique id checks in core and ACPI helpers
- Improvements to to Intel driver and cadence lib
* tag 'soundwire-5.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire:
soundwire: ignore uniqueID when irrelevant
soundwire: slave: add helper to extract slave ID
soundwire: remove bitfield for unique_id, use u8
soundwire: intel: fix PDI/stream mapping for Bulk
soundwire: cadence_master: make clock stop exit configurable on init
soundwire: intel/cadence: add flag for interrupt enable
soundwire: intel: add helper for initialization
soundwire: cadence_master: add hw_reset capability in debugfs
soundwire: intel/cadence: fix startup sequence
soundwire: intel: use correct header for io calls
soundwire: cadence_master: improve PDI allocation
soundwire: intel: don't filter out PDI0/1
soundwire: cadence/intel: simplify PDI/port mapping
soundwire: intel: remove playback/capture stream_name
soundwire: remove DAI_ID_RANGE definitions
soundwire: intel: remove X86 dependency
soundwire: intel: add missing headers for cross-compilation
-rw-r--r-- | drivers/soundwire/Kconfig | 2 | ||||
-rw-r--r-- | drivers/soundwire/bus.c | 7 | ||||
-rw-r--r-- | drivers/soundwire/cadence_master.c | 292 | ||||
-rw-r--r-- | drivers/soundwire/cadence_master.h | 39 | ||||
-rw-r--r-- | drivers/soundwire/intel.c | 201 | ||||
-rw-r--r-- | drivers/soundwire/intel_init.c | 1 | ||||
-rw-r--r-- | drivers/soundwire/slave.c | 98 | ||||
-rw-r--r-- | include/linux/soundwire/sdw.h | 7 |
8 files changed, 295 insertions, 352 deletions
diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index f518273cfbe3..26902023f4e7 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -23,7 +23,7 @@ config SOUNDWIRE_CADENCE config SOUNDWIRE_INTEL tristate "Intel SoundWire Master driver" select SOUNDWIRE_CADENCE - depends on X86 && ACPI && SND_SOC + depends on ACPI && SND_SOC help SoundWire Intel Master driver. If you have an Intel platform which has a SoundWire Master then diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fc53dbe57f85..be5d437058ed 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -422,10 +422,11 @@ static struct sdw_slave *sdw_get_slave(struct sdw_bus *bus, int i) 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 || + if (slave->id.mfg_id != id.mfg_id || slave->id.part_id != id.part_id || - slave->id.class_id != id.class_id) + slave->id.class_id != id.class_id || + (slave->id.unique_id != SDW_IGNORED_UNIQUE_ID && + slave->id.unique_id != id.unique_id)) return -ENODEV; return 0; diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 502ed4ec8f07..fed21e2b2277 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -183,9 +183,6 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); #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 /* @@ -232,6 +229,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) } /* + * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL + * need to be confirmed with a write to MCP_CONFIG_UPDATE + */ +static int cdns_update_config(struct sdw_cdns *cdns) +{ + int ret; + + ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, + CDNS_MCP_CONFIG_UPDATE_BIT); + if (ret < 0) + dev_err(cdns->dev, "Config update timedout\n"); + + return ret; +} + +/* * debugfs */ #ifdef CONFIG_DEBUG_FS @@ -279,11 +292,7 @@ static int cdns_reg_show(struct seq_file *s, void *data) ret += scnprintf(buf + ret, RD_BUF - ret, "\nDPn B0 Registers\n"); - /* - * in sdw_cdns_pdi_init() we filter out the Bulk PDIs, - * so the indices need to be corrected again - */ - num_ports = cdns->num_ports + CDNS_PCM_PDI_OFFSET; + num_ports = cdns->num_ports; for (i = 0; i < num_ports; i++) { ret += scnprintf(buf + ret, RD_BUF - ret, @@ -324,6 +333,26 @@ static int cdns_reg_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(cdns_reg); +static int cdns_hw_reset(void *data, u64 value) +{ + struct sdw_cdns *cdns = data; + int ret; + + if (value != 1) + return -EINVAL; + + /* Userspace changed the hardware state behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + + ret = sdw_cdns_exit_reset(cdns); + + dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); + /** * sdw_cdns_debugfs_init() - Cadence debugfs init * @cdns: Cadence instance @@ -332,6 +361,9 @@ DEFINE_SHOW_ATTRIBUTE(cdns_reg); void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) { debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); + + debugfs_create_file("cdns-hw-reset", 0200, root, cdns, + &cdns_hw_reset_fops); } EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); @@ -752,14 +784,48 @@ EXPORT_SYMBOL(sdw_cdns_thread); /* * init routines */ -static int _cdns_enable_interrupt(struct sdw_cdns *cdns) + +/** + * sdw_cdns_exit_reset() - Program reset parameters and start bus operations + * @cdns: Cadence instance + */ +int sdw_cdns_exit_reset(struct sdw_cdns *cdns) { - u32 mask; + /* program maximum length reset to be safe */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_RST_DELAY, + CDNS_MCP_CONTROL_RST_DELAY); + + /* use hardware generated reset */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_HW_RST, + CDNS_MCP_CONTROL_HW_RST); + + /* enable bus operations with clock and data */ + cdns_updatel(cdns, CDNS_MCP_CONFIG, + CDNS_MCP_CONFIG_OP, + CDNS_MCP_CONFIG_OP_NORMAL); + + /* commit changes */ + return cdns_update_config(cdns); +} +EXPORT_SYMBOL(sdw_cdns_exit_reset); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, - CDNS_MCP_SLAVE_INTMASK0_MASK); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, - CDNS_MCP_SLAVE_INTMASK1_MASK); +/** + * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config + * @cdns: Cadence instance + */ +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) +{ + u32 slave_intmask0 = 0; + u32 slave_intmask1 = 0; + u32 mask = 0; + + if (!state) + goto update_masks; + + slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK; + slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK; /* enable detection of all slave state changes */ mask = CDNS_MCP_INT_SLAVE_MASK; @@ -782,26 +848,13 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) if (interrupt_mask) /* parameter override */ mask = interrupt_mask; +update_masks: + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0); + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); 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\n"); - - return ret; + /* commit changes */ + return cdns_update_config(cdns); } EXPORT_SYMBOL(sdw_cdns_enable_interrupt); @@ -821,7 +874,6 @@ static int cdns_allocate_pdi(struct sdw_cdns *cdns, for (i = 0; i < num; i++) { pdi[i].num = i + pdi_offset; - pdi[i].assigned = false; } *stream = pdi; @@ -838,7 +890,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config) { struct sdw_cdns_streams *stream; - int offset, i, ret; + int offset; + int ret; cdns->pcm.num_bd = config.pcm_bd; cdns->pcm.num_in = config.pcm_in; @@ -850,11 +903,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, /* Allocate PDIs for PCMs */ stream = &cdns->pcm; - /* First two PDIs are reserved for bulk transfers */ - if (stream->num_bd < CDNS_PCM_PDI_OFFSET) - return -EINVAL; - stream->num_bd -= CDNS_PCM_PDI_OFFSET; - offset = CDNS_PCM_PDI_OFFSET; + /* we allocate PDI0 and PDI1 which are used for Bulk */ + offset = 0; ret = cdns_allocate_pdi(cdns, &stream->bd, stream->num_bd, offset); @@ -881,7 +931,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, /* 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) @@ -898,6 +947,9 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, ret = cdns_allocate_pdi(cdns, &stream->out, stream->num_out, offset); + + offset += stream->num_out; + if (ret) return ret; @@ -905,18 +957,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, 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); @@ -939,7 +979,7 @@ static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols) * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance */ -int sdw_cdns_init(struct sdw_cdns *cdns) +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit) { struct sdw_bus *bus = &cdns->bus; struct sdw_master_prop *prop = &bus->prop; @@ -947,12 +987,13 @@ int sdw_cdns_init(struct sdw_cdns *cdns) int divider; 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; + if (clock_stop_exit) { + 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 */ @@ -975,6 +1016,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL); + /* flush command FIFOs */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, + CDNS_MCP_CONTROL_CMD_RST); + /* Set cmd accept mode */ cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, CDNS_MCP_CONTROL_CMD_ACCEPT); @@ -997,13 +1042,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* 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; + /* commit changes */ + return cdns_update_config(cdns); } EXPORT_SYMBOL(sdw_cdns_init); @@ -1185,20 +1227,20 @@ EXPORT_SYMBOL(cdns_set_sdw_stream); * @num: Number of PDIs * @pdi: PDI instances * - * Find and return a free PDI for a given PDI array + * Find a PDI for a given PDI array. The PDI num and dai_id are + * expected to match, return NULL otherwise. */ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, + unsigned int offset, unsigned int num, - struct sdw_cdns_pdi *pdi) + struct sdw_cdns_pdi *pdi, + int dai_id) { int i; - for (i = 0; i < num; i++) { - if (pdi[i].assigned) - continue; - pdi[i].assigned = true; - return &pdi[i]; - } + for (i = offset; i < offset + num; i++) + if (pdi[i].num == dai_id) + return &pdi[i]; return NULL; } @@ -1207,13 +1249,11 @@ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, * 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; @@ -1221,113 +1261,51 @@ void sdw_cdns_config_stream(struct sdw_cdns *cdns, if (dir == SDW_DATA_DIR_RX) val = CDNS_PORTCTRL_DIRN; - offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET; + offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET; cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val); - val = port->num; + val = pdi->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) - 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 + * sdw_cdns_alloc_pdi() - Allocate a PDI * * @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 *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, + struct sdw_cdns_streams *stream, + u32 ch, u32 dir, int dai_id) { struct sdw_cdns_pdi *pdi = NULL; if (dir == SDW_DATA_DIR_RX) - pdi = cdns_find_pdi(cdns, stream->num_in, stream->in); + pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in, + dai_id); else - pdi = cdns_find_pdi(cdns, stream->num_out, stream->out); + pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out, + dai_id); /* 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; + pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd, + dai_id); + + if (pdi) { + pdi->l_ch_num = 0; + pdi->h_ch_num = ch - 1; + pdi->dir = dir; + pdi->ch_count = ch; + } - return 0; + return pdi; } -EXPORT_SYMBOL(sdw_cdns_alloc_stream); +EXPORT_SYMBOL(sdw_cdns_alloc_pdi); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Cadence Soundwire Library"); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 0b72b7094735..001457cbe5ad 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -8,7 +8,6 @@ /** * 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 @@ -18,7 +17,6 @@ * @type: stream type, PDM or PCM */ struct sdw_cdns_pdi { - bool assigned; int num; int intel_alh_id; int l_ch_num; @@ -29,23 +27,6 @@ struct sdw_cdns_pdi { }; /** - * 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 @@ -95,8 +76,8 @@ struct sdw_cdns_stream_config { * struct sdw_cdns_dma_data: Cadence DMA data * * @name: SoundWire stream name - * @nr_ports: Number of ports - * @port: Ports + * @stream: stream runtime + * @pdi: PDI used for this dai * @bus: Bus handle * @stream_type: Stream type * @link_id: Master link id @@ -104,8 +85,7 @@ struct sdw_cdns_stream_config { struct sdw_cdns_dma_data { char *name; struct sdw_stream_runtime *stream; - int nr_ports; - struct sdw_cdns_port **port; + struct sdw_cdns_pdi *pdi; struct sdw_bus *bus; enum sdw_stream_type stream_type; int link_id; @@ -158,10 +138,11 @@ 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_init(struct sdw_cdns *cdns, bool clock_stop_exit); 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_exit_reset(struct sdw_cdns *cdns); +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state); #ifdef CONFIG_DEBUG_FS void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); @@ -170,10 +151,10 @@ void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); 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, +struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, + struct sdw_cdns_streams *stream, + u32 ch, u32 dir, int dai_id); +void sdw_cdns_config_stream(struct sdw_cdns *cdns, u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai, diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index f1e38a293967..96f75bc32386 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/platform_device.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -479,7 +480,10 @@ intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) unsigned int link_id = sdw->instance; int pdi_conf = 0; - pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; + /* 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 @@ -508,7 +512,10 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) unsigned int link_id = sdw->instance; unsigned int conf; - pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; + /* 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)); @@ -603,66 +610,6 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * 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) - 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) @@ -670,9 +617,11 @@ static int intel_hw_params(struct snd_pcm_substream *substream, 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_cdns_pdi *pdi; struct sdw_stream_config sconfig; struct sdw_port_config *pconfig; - int ret, i, ch, dir; + int ch, dir; + int ret; bool pcm = true; dma = snd_soc_dai_get_dma_data(dai, substream); @@ -685,38 +634,30 @@ static int intel_hw_params(struct snd_pcm_substream *substream, 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); + if (dma->stream_type == SDW_STREAM_PDM) 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\n"); - return -EINVAL; + if (pcm) + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); + else + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id); + + if (!pdi) { + ret = -EINVAL; + goto error; } - dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL); - if (!dma->port) - return -ENOMEM; + /* do run-time configurations for SHIM, ALH and PDI/PORT */ + intel_pdi_shim_configure(sdw, pdi); + intel_pdi_alh_configure(sdw, pdi); + sdw_cdns_config_stream(cdns, ch, dir, pdi); - 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; - } + ret = intel_config_stream(sdw, substream, dai, params, + pdi->intel_alh_id); + if (ret) + goto error; sconfig.direction = dir; sconfig.ch_count = ch; @@ -731,32 +672,22 @@ static int intel_hw_params(struct snd_pcm_substream *substream, } /* Port configuration */ - pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL); + pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL); if (!pconfig) { ret = -ENOMEM; - goto port_error; + goto error; } - for (i = 0; i < dma->nr_ports; i++) { - pconfig[i].num = dma->port[i]->num; - pconfig[i].ch_mask = (1 << ch) - 1; - } + pconfig->num = pdi->num; + pconfig->ch_mask = (1 << ch) - 1; ret = sdw_stream_add_master(&cdns->bus, &sconfig, - pconfig, dma->nr_ports, dma->stream); - if (ret) { + pconfig, 1, dma->stream); + if (ret) dev_err(cdns->dev, "add master to stream failed:%d\n", ret); - goto stream_error; - } kfree(pconfig); - return ret; - -stream_error: - kfree(pconfig); -port_error: - intel_port_cleanup(dma); - kfree(dma->port); +error: return ret; } @@ -776,8 +707,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) dev_err(dai->dev, "remove master from stream %s failed: %d\n", dma->stream->name, ret); - intel_port_cleanup(dma); - kfree(dma->port); return ret; } @@ -842,14 +771,6 @@ static int intel_create_dai(struct sdw_cdns *cdns, 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; @@ -857,23 +778,12 @@ static int intel_create_dai(struct sdw_cdns *cdns, } 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 @@ -993,6 +903,15 @@ static struct sdw_master_ops sdw_intel_ops = { .post_bank_switch = intel_post_bank_switch, }; +static int intel_init(struct sdw_intel *sdw) +{ + /* Initialize shim and controller */ + intel_link_power_up(sdw); + intel_shim_init(sdw); + + return sdw_cdns_init(&sdw->cdns, false); +} + /* * probe and init */ @@ -1026,7 +945,7 @@ static int intel_probe(struct platform_device *pdev) 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; + return ret; } if (sdw->cdns.bus.prop.hw_disabled) { @@ -1035,16 +954,11 @@ static int intel_probe(struct platform_device *pdev) return 0; } - /* Initialize shim and controller */ - intel_link_power_up(sdw); - intel_shim_init(sdw); - - ret = sdw_cdns_init(&sdw->cdns); + /* Initialize shim, controller and Cadence IP */ + ret = intel_init(sdw); 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); @@ -1062,23 +976,35 @@ static int intel_probe(struct platform_device *pdev) goto err_init; } + ret = sdw_cdns_enable_interrupt(&sdw->cdns, true); + if (ret < 0) { + dev_err(sdw->cdns.dev, "cannot enable interrupts\n"); + goto err_init; + } + + ret = sdw_cdns_exit_reset(&sdw->cdns); + if (ret < 0) { + dev_err(sdw->cdns.dev, "unable to exit bus reset sequence\n"); + goto err_interrupt; + } + /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) { dev_err(sdw->cdns.dev, "DAI registration failed: %d\n", ret); snd_soc_unregister_component(sdw->cdns.dev); - goto err_dai; + goto err_interrupt; } intel_debugfs_init(sdw); return 0; -err_dai: +err_interrupt: + sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->res->irq, sdw); err_init: sdw_delete_bus_master(&sdw->cdns.bus); -err_master_reg: return ret; } @@ -1090,6 +1016,7 @@ static int intel_remove(struct platform_device *pdev) if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); + sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index b74c2f144962..2a2b4d8df462 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -9,6 +9,7 @@ #include <linux/acpi.h> #include <linux/export.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/soundwire/sdw_intel.h> diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 48a63ca130d2..e656ff57fc04 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -29,10 +29,17 @@ static int sdw_slave_add(struct sdw_bus *bus, 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); + if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { + /* name shall be sdw:link:mfg:part:class */ + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x", + bus->link_id, id->mfg_id, id->part_id, + id->class_id); + } else { + /* 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; @@ -64,6 +71,36 @@ static int sdw_slave_add(struct sdw_bus *bus, } #if IS_ENABLED(CONFIG_ACPI) + +static bool find_slave(struct sdw_bus *bus, + struct acpi_device *adev, + struct sdw_slave_id *id) +{ + unsigned long long addr; + 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 false; + } + + /* 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) + return false; + + sdw_extract_slave_id(bus, addr, id); + + return true; +} + /* * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node * @bus: SDW bus instance @@ -73,6 +110,7 @@ static int sdw_slave_add(struct sdw_bus *bus, int sdw_acpi_find_slaves(struct sdw_bus *bus) { struct acpi_device *adev, *parent; + struct acpi_device *adev2, *parent2; parent = ACPI_COMPANION(bus->dev); if (!parent) { @@ -81,28 +119,46 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus) } list_for_each_entry(adev, &parent->children, node) { - unsigned long long addr; struct sdw_slave_id id; - unsigned int link_id; - acpi_status status; + struct sdw_slave_id id2; + bool ignore_unique_id = true; - status = acpi_evaluate_integer(adev->handle, - METHOD_NAME__ADR, NULL, &addr); + if (!find_slave(bus, adev, &id)) + continue; - if (ACPI_FAILURE(status)) { - dev_err(bus->dev, "_ADR resolution failed: %x\n", - status); - return status; + /* brute-force O(N^2) search for duplicates */ + parent2 = parent; + list_for_each_entry(adev2, &parent2->children, node) { + + if (adev == adev2) + continue; + + if (!find_slave(bus, adev2, &id2)) + continue; + + if (id.sdw_version != id2.sdw_version || + id.mfg_id != id2.mfg_id || + id.part_id != id2.part_id || + id.class_id != id2.class_id) + continue; + + if (id.unique_id != id2.unique_id) { + dev_dbg(bus->dev, + "Valid unique IDs %x %x for Slave mfg %x part %d\n", + id.unique_id, id2.unique_id, + id.mfg_id, id.part_id); + ignore_unique_id = false; + } else { + dev_err(bus->dev, + "Invalid unique IDs %x %x for Slave mfg %x part %d\n", + id.unique_id, id2.unique_id, + id.mfg_id, id.part_id); + return -ENODEV; + } } - /* 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); + if (ignore_unique_id) + id.unique_id = SDW_IGNORED_UNIQUE_ID; /* * don't error check for sdw_slave_add as we want to continue diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index ea787201c3ac..28745b9ba279 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -40,9 +40,6 @@ struct sdw_slave; #define SDW_VALID_PORT_RANGE(n) ((n) <= 14 && (n) >= 1) -#define SDW_DAI_ID_RANGE_START 100 -#define SDW_DAI_ID_RANGE_END 200 - enum { SDW_PORT_DIRN_SINK = 0, SDW_PORT_DIRN_SOURCE, @@ -406,6 +403,8 @@ int sdw_slave_read_prop(struct sdw_slave *slave); * SDW Slave Structures and APIs */ +#define SDW_IGNORED_UNIQUE_ID 0xFF + /** * struct sdw_slave_id - Slave ID * @mfg_id: MIPI Manufacturer ID @@ -421,7 +420,7 @@ struct sdw_slave_id { __u16 mfg_id; __u16 part_id; __u8 class_id; - __u8 unique_id:4; + __u8 unique_id; __u8 sdw_version:4; }; |