diff mbox

[v2,10/13] soundwire: cdns: Add port routines

Message ID 1522946904-2089-11-git-send-email-vinod.koul@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vinod Koul April 5, 2018, 4:48 p.m. UTC
Add support for Cadence port management and implement
master port ops.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
 drivers/soundwire/cadence_master.c | 256 +++++++++++++++++++++++++++++++++++++
 drivers/soundwire/cadence_master.h | 100 +++++++++++++++
 drivers/soundwire/intel.c          |   1 +
 3 files changed, 357 insertions(+)

Comments

Pierre-Louis Bossart April 6, 2018, 12:19 a.m. UTC | #1
On 4/5/18 11:48 AM, Vinod Koul wrote:
> Add support for Cadence port management and implement
> master port ops.
> 
> Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
> Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> ---
>   drivers/soundwire/cadence_master.c | 256 +++++++++++++++++++++++++++++++++++++
>   drivers/soundwire/cadence_master.h | 100 +++++++++++++++
>   drivers/soundwire/intel.c          |   1 +
>   3 files changed, 357 insertions(+)
> 
> diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
> index b0c09efd8f83..89a4ae86d36a 100644
> --- a/drivers/soundwire/cadence_master.c
> +++ b/drivers/soundwire/cadence_master.c
> @@ -669,6 +669,133 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
>   }
>   EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
>   
> +static int cdns_allocate_pdi(struct sdw_cdns *cdns,
> +			struct sdw_cdns_pdi **stream,
> +			u32 start, u32 num, u32 pdi_offset)

the start parameter doesn't seem to be used, remove and remove the 
useless zeroes in all the calls to this routine?

> +{
> +	struct sdw_cdns_pdi *pdi;
> +	int i;
> +
> +	if (!num)
> +		return 0;
> +
> +	pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL);
> +	if (!pdi)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < num; i++) {
> +		pdi[i].num = i + pdi_offset;
> +		pdi[i].assigned = false;
> +	}
> +
> +	*stream = pdi;
> +	return 0;
> +}
> +
> +/**
> + * sdw_cdns_pdi_init: PDI initialization routine
> + *
> + * @cdns: Cadence instance
> + * @config: Stream configurations
> + */
> +int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
> +			struct sdw_cdns_stream_config config)
> +{
> +	struct sdw_cdns_streams *stream;
> +	int offset, i, ret;
> +
> +	cdns->pcm.num_bd = config.pcm_bd;
> +	cdns->pcm.num_in = config.pcm_in;
> +	cdns->pcm.num_out = config.pcm_out;
> +	cdns->pdm.num_bd = config.pdm_bd;
> +	cdns->pdm.num_in = config.pdm_in;
> +	cdns->pdm.num_out = config.pdm_out;
> +
> +	/* Allocate PDIs for PCMs */
> +	stream = &cdns->pcm;
> +
> +	/* First two PDIs are reserved for bulk transfers */
> +	stream->num_bd -= CDNS_PCM_PDI_OFFSET;
> +	offset = CDNS_PCM_PDI_OFFSET;
> +
> +	ret = cdns_allocate_pdi(cdns, &stream->bd, 0,
> +				stream->num_bd, offset);
> +	if (ret)
> +		goto pcm_error;
> +
> +	offset += stream->num_bd;
> +
> +	ret = cdns_allocate_pdi(cdns, &stream->in, 0,
> +				stream->num_in, offset);
> +	if (ret)
> +		goto pcm_error;
> +
> +
> +	offset += stream->num_in;
> +
> +	ret = cdns_allocate_pdi(cdns, &stream->out, 0,
> +				stream->num_out, offset);
> +	if (ret)
> +		goto pcm_error;
> +
> +	/* Update total number of PCM PDIs */
> +	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
> +	cdns->num_ports = stream->num_pdi;
> +
> +	/* Allocate PDIs for PDMs */
> +	stream = &cdns->pdm;
> +	offset = CDNS_PDM_PDI_OFFSET;
> +	ret = cdns_allocate_pdi(cdns, &stream->bd, 0,
> +				stream->num_bd, offset);
> +	if (ret)
> +		goto pdm_error;
> +
> +	offset += stream->num_bd;
> +
> +	ret = cdns_allocate_pdi(cdns, &stream->in, 0,
> +				stream->num_in, offset);
> +	if (ret)
> +		goto pdm_error;
> +
> +	offset += stream->num_in;
> +
> +	ret = cdns_allocate_pdi(cdns, &stream->out, 0,
> +				stream->num_out, offset);
> +	if (ret)
> +		goto pdm_error;
> +
> +	/* Update total number of PDM PDIs */
> +	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
> +	cdns->num_ports += stream->num_pdi;
> +
> +	cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports,
> +				sizeof(*cdns->ports), GFP_KERNEL);
> +	if (!cdns->ports) {
> +		ret = -ENOMEM;
> +		goto pdm_error;
> +	}
> +
> +	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;
> +
> +pdm_error:
> +	kfree(stream->bd);
> +	kfree(stream->in);
> +	kfree(stream->out);
> +
> +pcm_error:
> +	stream = &cdns->pcm;
> +	kfree(stream->bd);
> +	kfree(stream->in);
> +	kfree(stream->out);

call me a grumpy old fart if you want, I still don't like people freeing 
memory they never allocated. It may be legal but it's sloppy.

> +	return ret;
> +}
> +EXPORT_SYMBOL(sdw_cdns_pdi_init);
> +
>   /**
>    * sdw_cdns_init() - Cadence initialization
>    * @cdns: Cadence instance
> @@ -730,6 +857,134 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
>   }
>   EXPORT_SYMBOL(sdw_cdns_init);
>   
> +int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
> +{
> +	struct sdw_cdns *cdns = bus_to_cdns(bus);
> +	int mcp_clkctrl_off, mcp_clkctrl;
> +	int divider;
> +
> +	if (!params->curr_dr_freq) {
> +		dev_err(cdns->dev, "NULL curr_dr_freq");
> +		return -EINVAL;
> +	}
> +
> +	divider	= (params->max_dr_freq / params->curr_dr_freq) - 1;
> +
> +	if (params->next_bank)
> +		mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
> +	else
> +		mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
> +
> +	mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
> +	mcp_clkctrl |= divider;
> +	cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(cdns_bus_conf);
> +
> +static int cdns_port_params(struct sdw_bus *bus,
> +		struct sdw_port_params *p_params, unsigned int bank)
> +{
> +	struct sdw_cdns *cdns = bus_to_cdns(bus);
> +	int dpn_config = 0, dpn_config_off;
> +
> +	if (bank)
> +		dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num);
> +	else
> +		dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num);
> +
> +	dpn_config = cdns_readl(cdns, dpn_config_off);
> +
> +	dpn_config |= ((p_params->bps - 1) <<
> +				SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL));
> +	dpn_config |= (p_params->flow_mode <<
> +				SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW));
> +	dpn_config |= (p_params->data_mode <<
> +				SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT));
> +
> +	cdns_writel(cdns, dpn_config_off, dpn_config);
> +
> +	return 0;
> +}
> +
> +static int cdns_transport_params(struct sdw_bus *bus,
> +			struct sdw_transport_params *t_params,
> +			enum sdw_reg_bank bank)
> +{
> +	struct sdw_cdns *cdns = bus_to_cdns(bus);
> +	int dpn_offsetctrl = 0, dpn_offsetctrl_off;
> +	int dpn_config = 0, dpn_config_off;
> +	int dpn_hctrl = 0, dpn_hctrl_off;
> +	int num = t_params->port_num;
> +	int dpn_samplectrl_off;
> +
> +	/*
> +	 * Note: Only full data port is supported on the Master side for
> +	 * both PCM and PDM ports.
> +	 */
> +
> +	if (bank) {
> +		dpn_config_off = CDNS_DPN_B1_CONFIG(num);
> +		dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num);
> +		dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num);
> +		dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num);
> +	} else {
> +		dpn_config_off = CDNS_DPN_B0_CONFIG(num);
> +		dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num);
> +		dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num);
> +		dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num);
> +	}
> +
> +	dpn_config = cdns_readl(cdns, dpn_config_off);
> +
> +	dpn_config |= (t_params->blk_grp_ctrl <<
> +				SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC));
> +	dpn_config |= (t_params->blk_pkg_mode <<
> +				SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM));
> +	cdns_writel(cdns, dpn_config_off, dpn_config);
> +
> +	dpn_offsetctrl |= (t_params->offset1 <<
> +				SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1));
> +	dpn_offsetctrl |= (t_params->offset2 <<
> +				SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2));
> +	cdns_writel(cdns, dpn_offsetctrl_off,  dpn_offsetctrl);
> +
> +	dpn_hctrl |= (t_params->hstart <<
> +				SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART));
> +	dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP));
> +	dpn_hctrl |= (t_params->lane_ctrl <<
> +				SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL));
> +
> +	cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl);
> +	cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1));
> +
> +	return 0;
> +}
> +
> +static int cdns_port_enable(struct sdw_bus *bus,
> +		struct sdw_enable_ch *enable_ch, unsigned int bank)
> +{
> +	struct sdw_cdns *cdns = bus_to_cdns(bus);
> +	int dpn_chnen_off, ch_mask;
> +
> +	if (bank)
> +		dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->num);
> +	else
> +		dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->num);
> +
> +	ch_mask = enable_ch->ch_mask * enable_ch->enable;
> +	cdns_writel(cdns, dpn_chnen_off, ch_mask);
> +
> +	return 0;
> +}
> +
> +static const struct sdw_master_port_ops cdns_port_ops = {
> +	.dpn_set_port_params = cdns_port_params,
> +	.dpn_set_port_transport_params = cdns_transport_params,
> +	.dpn_port_enable_ch = cdns_port_enable,
> +};
> +
>   /**
>    * sdw_cdns_probe() - Cadence probe routine
>    * @cdns: Cadence instance
> @@ -737,6 +992,7 @@ EXPORT_SYMBOL(sdw_cdns_init);
>   int sdw_cdns_probe(struct sdw_cdns *cdns)
>   {
>   	init_completion(&cdns->tx_complete);
> +	cdns->bus.port_ops = &cdns_port_ops;
>   
>   	return 0;
>   }
> diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
> index 3ec74fa5f4f9..98a17f57918f 100644
> --- a/drivers/soundwire/cadence_master.h
> +++ b/drivers/soundwire/cadence_master.h
> @@ -5,6 +5,92 @@
>   #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 - Cadence driver context
>    * @dev: Linux device
>    * @bus: Bus handle
> @@ -12,6 +98,10 @@
>    * @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
> @@ -25,6 +115,12 @@ struct sdw_cdns {
>   	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;
> @@ -42,6 +138,8 @@ 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);
>   
>   enum sdw_command_response
> @@ -53,4 +151,6 @@ cdns_xfer_msg_defer(struct sdw_bus *bus,
>   
>   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);
>   #endif /* __SDW_CADENCE_H */
> diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
> index aa0c60133de5..a64f87a08cfd 100644
> --- a/drivers/soundwire/intel.c
> +++ b/drivers/soundwire/intel.c
> @@ -257,6 +257,7 @@ static struct sdw_master_ops sdw_intel_ops = {
>   	.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,
>   };
>   
>   /*
>
Vinod Koul April 6, 2018, 8:55 a.m. UTC | #2
On Thu, Apr 05, 2018 at 07:19:43PM -0500, Pierre-Louis Bossart wrote:
> On 4/5/18 11:48 AM, Vinod Koul wrote:

> >+static int cdns_allocate_pdi(struct sdw_cdns *cdns,
> >+			struct sdw_cdns_pdi **stream,
> >+			u32 start, u32 num, u32 pdi_offset)
> 
> the start parameter doesn't seem to be used, remove and remove the useless
> zeroes in all the calls to this routine?

Yes this seems to be the case, will fix

> >+
> >+pdm_error:
> >+	kfree(stream->bd);
> >+	kfree(stream->in);
> >+	kfree(stream->out);
> >+
> >+pcm_error:
> >+	stream = &cdns->pcm;
> >+	kfree(stream->bd);
> >+	kfree(stream->in);
> >+	kfree(stream->out);
> 
> call me a grumpy old fart if you want, I still don't like people freeing
> memory they never allocated. It may be legal but it's sloppy.

Actually looking at it freeing is not required as we are doing devm_
allocations, so this can be removed and return error should do the trick and
we propagate the error and cleanup, thanks for the catch
Pierre-Louis Bossart April 6, 2018, 3:29 p.m. UTC | #3
On 4/6/18 3:55 AM, Vinod Koul wrote:
> On Thu, Apr 05, 2018 at 07:19:43PM -0500, Pierre-Louis Bossart wrote:
>> On 4/5/18 11:48 AM, Vinod Koul wrote:
> 
>>> +static int cdns_allocate_pdi(struct sdw_cdns *cdns,
>>> +			struct sdw_cdns_pdi **stream,
>>> +			u32 start, u32 num, u32 pdi_offset)
>>
>> the start parameter doesn't seem to be used, remove and remove the useless
>> zeroes in all the calls to this routine?
> 
> Yes this seems to be the case, will fix
> 
>>> +
>>> +pdm_error:
>>> +	kfree(stream->bd);
>>> +	kfree(stream->in);
>>> +	kfree(stream->out);
>>> +
>>> +pcm_error:
>>> +	stream = &cdns->pcm;
>>> +	kfree(stream->bd);
>>> +	kfree(stream->in);
>>> +	kfree(stream->out);
>>
>> call me a grumpy old fart if you want, I still don't like people freeing
>> memory they never allocated. It may be legal but it's sloppy.
> 
> Actually looking at it freeing is not required as we are doing devm_
> allocations, so this can be removed and return error should do the trick and
> we propagate the error and cleanup, thanks for the catch

sounds good.
diff mbox

Patch

diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index b0c09efd8f83..89a4ae86d36a 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -669,6 +669,133 @@  int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns)
 }
 EXPORT_SYMBOL(sdw_cdns_enable_interrupt);
 
+static int cdns_allocate_pdi(struct sdw_cdns *cdns,
+			struct sdw_cdns_pdi **stream,
+			u32 start, u32 num, u32 pdi_offset)
+{
+	struct sdw_cdns_pdi *pdi;
+	int i;
+
+	if (!num)
+		return 0;
+
+	pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL);
+	if (!pdi)
+		return -ENOMEM;
+
+	for (i = 0; i < num; i++) {
+		pdi[i].num = i + pdi_offset;
+		pdi[i].assigned = false;
+	}
+
+	*stream = pdi;
+	return 0;
+}
+
+/**
+ * sdw_cdns_pdi_init: PDI initialization routine
+ *
+ * @cdns: Cadence instance
+ * @config: Stream configurations
+ */
+int sdw_cdns_pdi_init(struct sdw_cdns *cdns,
+			struct sdw_cdns_stream_config config)
+{
+	struct sdw_cdns_streams *stream;
+	int offset, i, ret;
+
+	cdns->pcm.num_bd = config.pcm_bd;
+	cdns->pcm.num_in = config.pcm_in;
+	cdns->pcm.num_out = config.pcm_out;
+	cdns->pdm.num_bd = config.pdm_bd;
+	cdns->pdm.num_in = config.pdm_in;
+	cdns->pdm.num_out = config.pdm_out;
+
+	/* Allocate PDIs for PCMs */
+	stream = &cdns->pcm;
+
+	/* First two PDIs are reserved for bulk transfers */
+	stream->num_bd -= CDNS_PCM_PDI_OFFSET;
+	offset = CDNS_PCM_PDI_OFFSET;
+
+	ret = cdns_allocate_pdi(cdns, &stream->bd, 0,
+				stream->num_bd, offset);
+	if (ret)
+		goto pcm_error;
+
+	offset += stream->num_bd;
+
+	ret = cdns_allocate_pdi(cdns, &stream->in, 0,
+				stream->num_in, offset);
+	if (ret)
+		goto pcm_error;
+
+
+	offset += stream->num_in;
+
+	ret = cdns_allocate_pdi(cdns, &stream->out, 0,
+				stream->num_out, offset);
+	if (ret)
+		goto pcm_error;
+
+	/* Update total number of PCM PDIs */
+	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
+	cdns->num_ports = stream->num_pdi;
+
+	/* Allocate PDIs for PDMs */
+	stream = &cdns->pdm;
+	offset = CDNS_PDM_PDI_OFFSET;
+	ret = cdns_allocate_pdi(cdns, &stream->bd, 0,
+				stream->num_bd, offset);
+	if (ret)
+		goto pdm_error;
+
+	offset += stream->num_bd;
+
+	ret = cdns_allocate_pdi(cdns, &stream->in, 0,
+				stream->num_in, offset);
+	if (ret)
+		goto pdm_error;
+
+	offset += stream->num_in;
+
+	ret = cdns_allocate_pdi(cdns, &stream->out, 0,
+				stream->num_out, offset);
+	if (ret)
+		goto pdm_error;
+
+	/* Update total number of PDM PDIs */
+	stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out;
+	cdns->num_ports += stream->num_pdi;
+
+	cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports,
+				sizeof(*cdns->ports), GFP_KERNEL);
+	if (!cdns->ports) {
+		ret = -ENOMEM;
+		goto pdm_error;
+	}
+
+	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;
+
+pdm_error:
+	kfree(stream->bd);
+	kfree(stream->in);
+	kfree(stream->out);
+
+pcm_error:
+	stream = &cdns->pcm;
+	kfree(stream->bd);
+	kfree(stream->in);
+	kfree(stream->out);
+	return ret;
+}
+EXPORT_SYMBOL(sdw_cdns_pdi_init);
+
 /**
  * sdw_cdns_init() - Cadence initialization
  * @cdns: Cadence instance
@@ -730,6 +857,134 @@  int sdw_cdns_init(struct sdw_cdns *cdns)
 }
 EXPORT_SYMBOL(sdw_cdns_init);
 
+int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int mcp_clkctrl_off, mcp_clkctrl;
+	int divider;
+
+	if (!params->curr_dr_freq) {
+		dev_err(cdns->dev, "NULL curr_dr_freq");
+		return -EINVAL;
+	}
+
+	divider	= (params->max_dr_freq / params->curr_dr_freq) - 1;
+
+	if (params->next_bank)
+		mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1;
+	else
+		mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0;
+
+	mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off);
+	mcp_clkctrl |= divider;
+	cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl);
+
+	return 0;
+}
+EXPORT_SYMBOL(cdns_bus_conf);
+
+static int cdns_port_params(struct sdw_bus *bus,
+		struct sdw_port_params *p_params, unsigned int bank)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int dpn_config = 0, dpn_config_off;
+
+	if (bank)
+		dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num);
+	else
+		dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num);
+
+	dpn_config = cdns_readl(cdns, dpn_config_off);
+
+	dpn_config |= ((p_params->bps - 1) <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL));
+	dpn_config |= (p_params->flow_mode <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW));
+	dpn_config |= (p_params->data_mode <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT));
+
+	cdns_writel(cdns, dpn_config_off, dpn_config);
+
+	return 0;
+}
+
+static int cdns_transport_params(struct sdw_bus *bus,
+			struct sdw_transport_params *t_params,
+			enum sdw_reg_bank bank)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int dpn_offsetctrl = 0, dpn_offsetctrl_off;
+	int dpn_config = 0, dpn_config_off;
+	int dpn_hctrl = 0, dpn_hctrl_off;
+	int num = t_params->port_num;
+	int dpn_samplectrl_off;
+
+	/*
+	 * Note: Only full data port is supported on the Master side for
+	 * both PCM and PDM ports.
+	 */
+
+	if (bank) {
+		dpn_config_off = CDNS_DPN_B1_CONFIG(num);
+		dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num);
+		dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num);
+		dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num);
+	} else {
+		dpn_config_off = CDNS_DPN_B0_CONFIG(num);
+		dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num);
+		dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num);
+		dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num);
+	}
+
+	dpn_config = cdns_readl(cdns, dpn_config_off);
+
+	dpn_config |= (t_params->blk_grp_ctrl <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC));
+	dpn_config |= (t_params->blk_pkg_mode <<
+				SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM));
+	cdns_writel(cdns, dpn_config_off, dpn_config);
+
+	dpn_offsetctrl |= (t_params->offset1 <<
+				SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1));
+	dpn_offsetctrl |= (t_params->offset2 <<
+				SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2));
+	cdns_writel(cdns, dpn_offsetctrl_off,  dpn_offsetctrl);
+
+	dpn_hctrl |= (t_params->hstart <<
+				SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART));
+	dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP));
+	dpn_hctrl |= (t_params->lane_ctrl <<
+				SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL));
+
+	cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl);
+	cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1));
+
+	return 0;
+}
+
+static int cdns_port_enable(struct sdw_bus *bus,
+		struct sdw_enable_ch *enable_ch, unsigned int bank)
+{
+	struct sdw_cdns *cdns = bus_to_cdns(bus);
+	int dpn_chnen_off, ch_mask;
+
+	if (bank)
+		dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->num);
+	else
+		dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->num);
+
+	ch_mask = enable_ch->ch_mask * enable_ch->enable;
+	cdns_writel(cdns, dpn_chnen_off, ch_mask);
+
+	return 0;
+}
+
+static const struct sdw_master_port_ops cdns_port_ops = {
+	.dpn_set_port_params = cdns_port_params,
+	.dpn_set_port_transport_params = cdns_transport_params,
+	.dpn_port_enable_ch = cdns_port_enable,
+};
+
 /**
  * sdw_cdns_probe() - Cadence probe routine
  * @cdns: Cadence instance
@@ -737,6 +992,7 @@  EXPORT_SYMBOL(sdw_cdns_init);
 int sdw_cdns_probe(struct sdw_cdns *cdns)
 {
 	init_completion(&cdns->tx_complete);
+	cdns->bus.port_ops = &cdns_port_ops;
 
 	return 0;
 }
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index 3ec74fa5f4f9..98a17f57918f 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -5,6 +5,92 @@ 
 #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 - Cadence driver context
  * @dev: Linux device
  * @bus: Bus handle
@@ -12,6 +98,10 @@ 
  * @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
@@ -25,6 +115,12 @@  struct sdw_cdns {
 	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;
@@ -42,6 +138,8 @@  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);
 
 enum sdw_command_response
@@ -53,4 +151,6 @@  cdns_xfer_msg_defer(struct sdw_bus *bus,
 
 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);
 #endif /* __SDW_CADENCE_H */
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index aa0c60133de5..a64f87a08cfd 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -257,6 +257,7 @@  static struct sdw_master_ops sdw_intel_ops = {
 	.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,
 };
 
 /*