Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d6b3c8b authored by Vinod Koul's avatar Vinod Koul
Browse files

soundwire: cdns: Add stream routines



Add support for Cadence stream initialization and implement
stream APIs.

Signed-off-by: default avatarSanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: default avatarShreyas NC <shreyas.nc@intel.com>
Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 07abeff1
Loading
Loading
Loading
Loading
+195 −0
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@
#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "bus.h"
#include "cadence_master.h"

@@ -985,5 +987,198 @@ int sdw_cdns_probe(struct sdw_cdns *cdns)
}
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");
+43 −0
Original line number Diff line number Diff line
// 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
@@ -90,6 +91,26 @@ struct sdw_cdns_stream_config {
	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
@@ -142,6 +163,25 @@ 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);

@@ -153,4 +193,7 @@ 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 */