diff mbox

[v2,04/13] soundwire: Add Master and Slave port programming

Message ID 1522946904-2089-5-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
From: Sanyog Kale <sanyog.r.kale@intel.com>

Master and Slave port registers need to be programmed for each port
used in a stream. Add the helpers for port register programming.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
---
 drivers/soundwire/bus.h       |   4 +
 drivers/soundwire/stream.c    | 262 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw.h |  47 +++++++-
 3 files changed, 312 insertions(+), 1 deletion(-)

Comments

Pierre-Louis Bossart April 5, 2018, 11:14 p.m. UTC | #1
On 4/5/18 11:48 AM, Vinod Koul wrote:
> From: Sanyog Kale <sanyog.r.kale@intel.com>
> 
> Master and Slave port registers need to be programmed for each port
> used in a stream. Add the helpers for port register programming.
> 
> Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
> ---
>   drivers/soundwire/bus.h       |   4 +
>   drivers/soundwire/stream.c    | 262 ++++++++++++++++++++++++++++++++++++++++++
>   include/linux/soundwire/sdw.h |  47 +++++++-
>   3 files changed, 312 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
> index 2e834a8038ed..33dc31c8f992 100644
> --- a/drivers/soundwire/bus.h
> +++ b/drivers/soundwire/bus.h
> @@ -106,6 +106,10 @@ struct sdw_master_runtime {
>   	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_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);
> diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
> index 4b10f07b3e9e..61417a4decc5 100644
> --- a/drivers/soundwire/stream.c
> +++ b/drivers/soundwire/stream.c
> @@ -11,9 +11,238 @@
>   #include <linux/module.h>
>   #include <linux/mod_devicetable.h>
>   #include <linux/slab.h>
> +#include <linux/soundwire/sdw_registers.h>
>   #include <linux/soundwire/sdw.h>
>   #include "bus.h"
>   
> +static int _sdw_program_slave_port_params(struct sdw_bus *bus,
> +				struct sdw_slave *slave,
> +				struct sdw_transport_params *t_params,
> +				enum sdw_dpn_type type)
> +{
> +	u32 addr1, addr2, addr3, addr4;
> +	int ret;
> +	u8 wbuf;
> +
> +	if (bus->params.next_bank) {
> +		addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num);
> +		addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num);
> +		addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num);
> +		addr4 = SDW_DPN_HCTRL_B1(t_params->port_num);
> +	} else {
> +		addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num);
> +		addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num);
> +		addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num);
> +		addr4 = SDW_DPN_HCTRL_B0(t_params->port_num);
> +	}
> +
> +	/* Program DPN_OffsetCtrl2 registers */
> +	ret = sdw_write(slave, addr1, t_params->offset2);
> +	if (ret < 0) {
> +		dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed");
> +		return ret;
> +	}
> +
> +	/* Program DPN_BlockCtrl3 register */
> +	ret = sdw_write(slave, addr2, t_params->blk_pkg_mode);
> +	if (ret < 0) {
> +		dev_err(bus->dev, "DPN_BlockCtrl3 register write failed");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Data ports are FULL, SIMPLE and REDUCED. This function handles
> +	 * FULL and REDUCED only and and beyond this point only FULL is
> +	 * handled, so bail out if we are not FULL data port type
> +	 */
> +	if (type != SDW_DPN_FULL)
> +		return ret;
> +
> +	/* Program DPN_SampleCtrl2 register */
> +	wbuf = ((t_params->sample_interval - 1) &
> +			SDW_DPN_SAMPLECTRL_HIGH) >>
> +			SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);

	wbuf = t_params->sample_interval - 1;
	wbuf &= SDW_DPN_SAMPLECTRL_HIGH);
	wbuf >>= SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);

same lines of code, simpler to read?

> +
> +	ret = sdw_write(slave, addr3, wbuf);
> +	if (ret < 0) {
> +		dev_err(bus->dev, "DPN_SampleCtrl2 register write failed");
> +		return ret;
> +	}
> +
> +	/* Program DPN_HCtrl register */
> +	wbuf = (t_params->hstop | (t_params->hstart <<
> +				SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART)));

eyesore again.

> +	ret = sdw_write(slave, addr4, wbuf);
> +	if (ret < 0)
> +		dev_err(bus->dev, "DPN_HCtrl register write failed");
> +
> +	return ret;
> +}
> +
> +static int sdw_program_slave_port_params(struct sdw_bus *bus,
> +			struct sdw_slave_runtime *s_rt,
> +			struct sdw_port_runtime *p_rt)
> +{
> +	struct sdw_transport_params *t_params = &p_rt->transport_params;
> +	struct sdw_port_params *p_params = &p_rt->port_params;
> +	struct sdw_slave_prop *slave_prop = &s_rt->slave->prop;
> +	u32 addr1, addr2, addr3, addr4, addr5, addr6;
> +	struct sdw_dpn_prop *dpn_prop;
> +	int ret;
> +	u8 wbuf;
> +
> +	dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
> +					s_rt->direction,
> +					t_params->port_num);
> +	if (!dpn_prop)
> +		return -EINVAL;
> +
> +	addr1 = SDW_DPN_PORTCTRL(t_params->port_num);
> +	addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num);
> +
> +	if (bus->params.next_bank) {
> +		addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num);
> +		addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num);
> +		addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num);
> +		addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num);
> +
> +	} else {
> +		addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num);
> +		addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num);
> +		addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num);
> +		addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num);
> +	}
> +
> +	/* Program DPN_PortCtrl register */
> +	wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE);
> +	wbuf |= p_params->flow_mode;
> +
> +	ret = sdw_update(s_rt->slave, addr1, 0xF, wbuf);
> +	if (ret < 0) {
> +		dev_err(&s_rt->slave->dev,
> +			"DPN_PortCtrl register write failed for port %d",
> +			t_params->port_num);
> +		return ret;
> +	}
> +
> +	/* Program DPN_BlockCtrl1 register */
> +	ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1));
> +	if (ret < 0) {
> +		dev_err(&s_rt->slave->dev,
> +			"DPN_BlockCtrl1 register write failed for port %d",
> +			t_params->port_num);
> +		return ret;
> +	}
> +
> +	/* Program DPN_SampleCtrl1 register */
> +	wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW;
> +	ret = sdw_write(s_rt->slave, addr3, wbuf);
> +	if (ret < 0) {
> +		dev_err(&s_rt->slave->dev,
> +			"DPN_SampleCtrl1 register write failed for port %d",
> +			t_params->port_num);
> +		return ret;
> +	}
> +
> +	/* Program DPN_OffsetCtrl1 registers */
> +	ret = sdw_write(s_rt->slave, addr4, t_params->offset1);
> +	if (ret < 0) {
> +		dev_err(&s_rt->slave->dev,
> +			"DPN_OffsetCtrl1 register write failed for port %d",
> +			t_params->port_num);
> +		return ret;
> +	}
> +
> +	/* Program DPN_BlockCtrl2 register*/
> +	if (t_params->blk_grp_ctrl_valid) {
> +		ret = sdw_write(s_rt->slave, addr5, t_params->blk_grp_ctrl);
> +		if (ret < 0) {
> +			dev_err(&s_rt->slave->dev,
> +				"DPN_BlockCtrl2 reg write failed for port %d",
> +				t_params->port_num);
> +			return ret;
> +		}
> +	}
> +
> +	/* program DPN_LaneCtrl register */
> +	if (slave_prop->lane_control_support) {
> +		ret = sdw_write(s_rt->slave, addr6, t_params->lane_ctrl);
> +		if (ret < 0) {
> +			dev_err(&s_rt->slave->dev,
> +				"DPN_LaneCtrl register write failed for port %d",
> +				t_params->port_num);
> +			return ret;
> +		}
> +	}
> +
> +	if (dpn_prop->type != SDW_DPN_SIMPLE) {
> +		ret = _sdw_program_slave_port_params(bus, s_rt->slave,
> +						t_params, dpn_prop->type);
> +		if (ret < 0)
> +			dev_err(&s_rt->slave->dev,
> +				"Transport reg write failed for port: %d",
> +				t_params->port_num);
> +	}
> +
> +	return ret;
> +}
> +
> +static int sdw_program_master_port_params(struct sdw_bus *bus,
> +		struct sdw_port_runtime *p_rt)
> +{
> +	int ret;
> +
> +	/*
> +	 * we need to set transport and port parameters for the port.
> +	 * Transport parameters refers to the smaple interval, offsets and
> +	 * hstart/stop etc of the data. Port parameters refers to word
> +	 * length, flow mode etc of the port
> +	 */
> +	ret = bus->port_ops->dpn_set_port_transport_params(bus,
> +					&p_rt->transport_params,
> +					bus->params.next_bank);
> +	if (ret < 0)
> +		return ret;
> +
> +	return bus->port_ops->dpn_set_port_params(bus,
> +				&p_rt->port_params,
> +				bus->params.next_bank);
> +}
> +
> +/**
> + * sdw_program_port_params: Programs transport parameters of Master(s)
> + * and Slave(s)
> + *
> + * @m_rt: Master stream runtime
> + */
> +static int sdw_program_port_params(struct sdw_master_runtime *m_rt)
> +{
> +	struct sdw_slave_runtime *s_rt = NULL;
> +	struct sdw_bus *bus = m_rt->bus;
> +	struct sdw_port_runtime *p_rt;
> +	int ret = 0;
> +
> +	/* Program transport & port parameters for Slave(s) */
> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
> +
> +			ret = sdw_program_slave_port_params(bus, s_rt, p_rt);
> +			if (ret < 0)
> +				return ret;
> +
> +		}
> +	}
> +
> +	/* Program transport & port parameters for Master(s) */
> +	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
> +		ret = sdw_program_master_port_params(bus, p_rt);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
>   /**
>    * sdw_release_stream: Free the assigned stream runtime
>    *
> @@ -499,3 +728,36 @@ int sdw_stream_add_slave(struct sdw_slave *slave,
>   	return ret;
>   }
>   EXPORT_SYMBOL(sdw_stream_add_slave);
> +
> +/**
> + * sdw_get_slave_dpn_prop: Get Slave port capabilities
> + *
> + * @slave: Slave handle
> + * @direction: Data direction.
> + * @port_num: Port number
> + */
> +struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
> +				enum sdw_data_direction direction,
> +				unsigned int port_num)
> +{
> +	struct sdw_dpn_prop *dpn_prop;
> +	u8 num_ports;
> +	int i;
> +
> +	if (direction == SDW_DATA_DIR_TX) {
> +		num_ports = hweight32(slave->prop.source_ports);
> +		dpn_prop = slave->prop.src_dpn_prop;
> +	} else {
> +		num_ports = hweight32(slave->prop.sink_ports);
> +		dpn_prop = slave->prop.sink_dpn_prop;
> +	}
> +
> +	for (i = 0; i < num_ports; i++) {
> +		dpn_prop = &dpn_prop[i];
> +
> +		if (dpn_prop->num == port_num)
> +			return &dpn_prop[i];
> +	}
> +
> +	return NULL;
> +}
> diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
> index 228fdbf506fe..f2b952148e1a 100644
> --- a/include/linux/soundwire/sdw.h
> +++ b/include/linux/soundwire/sdw.h
> @@ -367,7 +367,30 @@ struct sdw_slave_intr_status {
>   };
>   
>   /**
> - * struct sdw_slave_ops - Slave driver callback ops
> + * sdw_reg_bank - SoundWire register banks
> + * @SDW_BANK0: Soundwire register bank 0
> + * @SDW_BANK1: Soundwire register bank 1
> + */
> +enum sdw_reg_bank {
> +	SDW_BANK0,
> +	SDW_BANK1,
> +};
> +
> +/**
> + * struct sdw_bus_params: Structure holding bus configuration
> + *
> + * @curr_bank: Current bank in use (BANK0/BANK1)
> + * @next_bank: Next bank to use (BANK0/BANK1). next_bank will always be
> + * set to !curr_bank
> + */
> +struct sdw_bus_params {
> +	enum sdw_reg_bank curr_bank;
> +	enum sdw_reg_bank next_bank;
> +};
> +
> +/**
> + * struct sdw_slave_ops: Slave driver callback ops
> + *
>    * @read_prop: Read Slave properties
>    * @interrupt_callback: Device interrupt notification (invoked in thread
>    * context)
> @@ -482,6 +505,24 @@ struct sdw_transport_params {
>   	unsigned int lane_ctrl;
>   };
>   
> +/**
> + * struct sdw_master_port_ops: Callback functions from bus to Master
> + * driver to set Master Data ports.
> + *
> + * @dpn_set_port_params: Set the Port parameters for the Master Port.
> + * Mandatory callback
> + * @dpn_set_port_transport_params: Set transport parameters for the Master
> + * Port. Mandatory callback
> + */
> +struct sdw_master_port_ops {
> +	int (*dpn_set_port_params)(struct sdw_bus *bus,
> +			struct sdw_port_params *port_params,
> +			unsigned int bank);
> +	int (*dpn_set_port_transport_params)(struct sdw_bus *bus,
> +			struct sdw_transport_params *transport_params,
> +			enum sdw_reg_bank bank);
> +};
> +
>   struct sdw_msg;
>   
>   /**
> @@ -525,6 +566,8 @@ struct sdw_master_ops {
>    * @bus_lock: bus lock
>    * @msg_lock: message lock
>    * @ops: Master callback ops
> + * @port_ops: Master port callback ops
> + * @params: Current bus parameters
>    * @prop: Master properties
>    * @m_rt_list: List of Master instance of all stream(s) running on Bus. This
>    * is used to compute and program bus bandwidth, clock, frame shape,
> @@ -540,6 +583,8 @@ struct sdw_bus {
>   	struct mutex bus_lock;
>   	struct mutex msg_lock;
>   	const struct sdw_master_ops *ops;
> +	const struct sdw_master_port_ops *port_ops;
> +	struct sdw_bus_params params;
>   	struct sdw_master_prop prop;
>   	struct list_head m_rt_list;
>   	struct sdw_defer defer_msg;
>
Vinod Koul April 6, 2018, 5:01 a.m. UTC | #2
On Thu, Apr 05, 2018 at 06:14:58PM -0500, Pierre-Louis Bossart wrote:
> On 4/5/18 11:48 AM, Vinod Koul wrote:

> >+	/* Program DPN_SampleCtrl2 register */
> >+	wbuf = ((t_params->sample_interval - 1) &
> >+			SDW_DPN_SAMPLECTRL_HIGH) >>
> >+			SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);
> 
> 	wbuf = t_params->sample_interval - 1;
> 	wbuf &= SDW_DPN_SAMPLECTRL_HIGH);
> 	wbuf >>= SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);
> 
> same lines of code, simpler to read?

ok will fix both of these
diff mbox

Patch

diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
index 2e834a8038ed..33dc31c8f992 100644
--- a/drivers/soundwire/bus.h
+++ b/drivers/soundwire/bus.h
@@ -106,6 +106,10 @@  struct sdw_master_runtime {
 	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_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);
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index 4b10f07b3e9e..61417a4decc5 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -11,9 +11,238 @@ 
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/slab.h>
+#include <linux/soundwire/sdw_registers.h>
 #include <linux/soundwire/sdw.h>
 #include "bus.h"
 
+static int _sdw_program_slave_port_params(struct sdw_bus *bus,
+				struct sdw_slave *slave,
+				struct sdw_transport_params *t_params,
+				enum sdw_dpn_type type)
+{
+	u32 addr1, addr2, addr3, addr4;
+	int ret;
+	u8 wbuf;
+
+	if (bus->params.next_bank) {
+		addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num);
+		addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num);
+		addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num);
+		addr4 = SDW_DPN_HCTRL_B1(t_params->port_num);
+	} else {
+		addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num);
+		addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num);
+		addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num);
+		addr4 = SDW_DPN_HCTRL_B0(t_params->port_num);
+	}
+
+	/* Program DPN_OffsetCtrl2 registers */
+	ret = sdw_write(slave, addr1, t_params->offset2);
+	if (ret < 0) {
+		dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed");
+		return ret;
+	}
+
+	/* Program DPN_BlockCtrl3 register */
+	ret = sdw_write(slave, addr2, t_params->blk_pkg_mode);
+	if (ret < 0) {
+		dev_err(bus->dev, "DPN_BlockCtrl3 register write failed");
+		return ret;
+	}
+
+	/*
+	 * Data ports are FULL, SIMPLE and REDUCED. This function handles
+	 * FULL and REDUCED only and and beyond this point only FULL is
+	 * handled, so bail out if we are not FULL data port type
+	 */
+	if (type != SDW_DPN_FULL)
+		return ret;
+
+	/* Program DPN_SampleCtrl2 register */
+	wbuf = ((t_params->sample_interval - 1) &
+			SDW_DPN_SAMPLECTRL_HIGH) >>
+			SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH);
+
+	ret = sdw_write(slave, addr3, wbuf);
+	if (ret < 0) {
+		dev_err(bus->dev, "DPN_SampleCtrl2 register write failed");
+		return ret;
+	}
+
+	/* Program DPN_HCtrl register */
+	wbuf = (t_params->hstop | (t_params->hstart <<
+				SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART)));
+	ret = sdw_write(slave, addr4, wbuf);
+	if (ret < 0)
+		dev_err(bus->dev, "DPN_HCtrl register write failed");
+
+	return ret;
+}
+
+static int sdw_program_slave_port_params(struct sdw_bus *bus,
+			struct sdw_slave_runtime *s_rt,
+			struct sdw_port_runtime *p_rt)
+{
+	struct sdw_transport_params *t_params = &p_rt->transport_params;
+	struct sdw_port_params *p_params = &p_rt->port_params;
+	struct sdw_slave_prop *slave_prop = &s_rt->slave->prop;
+	u32 addr1, addr2, addr3, addr4, addr5, addr6;
+	struct sdw_dpn_prop *dpn_prop;
+	int ret;
+	u8 wbuf;
+
+	dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
+					s_rt->direction,
+					t_params->port_num);
+	if (!dpn_prop)
+		return -EINVAL;
+
+	addr1 = SDW_DPN_PORTCTRL(t_params->port_num);
+	addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num);
+
+	if (bus->params.next_bank) {
+		addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num);
+		addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num);
+		addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num);
+		addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num);
+
+	} else {
+		addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num);
+		addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num);
+		addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num);
+		addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num);
+	}
+
+	/* Program DPN_PortCtrl register */
+	wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE);
+	wbuf |= p_params->flow_mode;
+
+	ret = sdw_update(s_rt->slave, addr1, 0xF, wbuf);
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_PortCtrl register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_BlockCtrl1 register */
+	ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1));
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_BlockCtrl1 register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_SampleCtrl1 register */
+	wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW;
+	ret = sdw_write(s_rt->slave, addr3, wbuf);
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_SampleCtrl1 register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_OffsetCtrl1 registers */
+	ret = sdw_write(s_rt->slave, addr4, t_params->offset1);
+	if (ret < 0) {
+		dev_err(&s_rt->slave->dev,
+			"DPN_OffsetCtrl1 register write failed for port %d",
+			t_params->port_num);
+		return ret;
+	}
+
+	/* Program DPN_BlockCtrl2 register*/
+	if (t_params->blk_grp_ctrl_valid) {
+		ret = sdw_write(s_rt->slave, addr5, t_params->blk_grp_ctrl);
+		if (ret < 0) {
+			dev_err(&s_rt->slave->dev,
+				"DPN_BlockCtrl2 reg write failed for port %d",
+				t_params->port_num);
+			return ret;
+		}
+	}
+
+	/* program DPN_LaneCtrl register */
+	if (slave_prop->lane_control_support) {
+		ret = sdw_write(s_rt->slave, addr6, t_params->lane_ctrl);
+		if (ret < 0) {
+			dev_err(&s_rt->slave->dev,
+				"DPN_LaneCtrl register write failed for port %d",
+				t_params->port_num);
+			return ret;
+		}
+	}
+
+	if (dpn_prop->type != SDW_DPN_SIMPLE) {
+		ret = _sdw_program_slave_port_params(bus, s_rt->slave,
+						t_params, dpn_prop->type);
+		if (ret < 0)
+			dev_err(&s_rt->slave->dev,
+				"Transport reg write failed for port: %d",
+				t_params->port_num);
+	}
+
+	return ret;
+}
+
+static int sdw_program_master_port_params(struct sdw_bus *bus,
+		struct sdw_port_runtime *p_rt)
+{
+	int ret;
+
+	/*
+	 * we need to set transport and port parameters for the port.
+	 * Transport parameters refers to the smaple interval, offsets and
+	 * hstart/stop etc of the data. Port parameters refers to word
+	 * length, flow mode etc of the port
+	 */
+	ret = bus->port_ops->dpn_set_port_transport_params(bus,
+					&p_rt->transport_params,
+					bus->params.next_bank);
+	if (ret < 0)
+		return ret;
+
+	return bus->port_ops->dpn_set_port_params(bus,
+				&p_rt->port_params,
+				bus->params.next_bank);
+}
+
+/**
+ * sdw_program_port_params: Programs transport parameters of Master(s)
+ * and Slave(s)
+ *
+ * @m_rt: Master stream runtime
+ */
+static int sdw_program_port_params(struct sdw_master_runtime *m_rt)
+{
+	struct sdw_slave_runtime *s_rt = NULL;
+	struct sdw_bus *bus = m_rt->bus;
+	struct sdw_port_runtime *p_rt;
+	int ret = 0;
+
+	/* Program transport & port parameters for Slave(s) */
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+
+			ret = sdw_program_slave_port_params(bus, s_rt, p_rt);
+			if (ret < 0)
+				return ret;
+
+		}
+	}
+
+	/* Program transport & port parameters for Master(s) */
+	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+		ret = sdw_program_master_port_params(bus, p_rt);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
 /**
  * sdw_release_stream: Free the assigned stream runtime
  *
@@ -499,3 +728,36 @@  int sdw_stream_add_slave(struct sdw_slave *slave,
 	return ret;
 }
 EXPORT_SYMBOL(sdw_stream_add_slave);
+
+/**
+ * sdw_get_slave_dpn_prop: Get Slave port capabilities
+ *
+ * @slave: Slave handle
+ * @direction: Data direction.
+ * @port_num: Port number
+ */
+struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
+				enum sdw_data_direction direction,
+				unsigned int port_num)
+{
+	struct sdw_dpn_prop *dpn_prop;
+	u8 num_ports;
+	int i;
+
+	if (direction == SDW_DATA_DIR_TX) {
+		num_ports = hweight32(slave->prop.source_ports);
+		dpn_prop = slave->prop.src_dpn_prop;
+	} else {
+		num_ports = hweight32(slave->prop.sink_ports);
+		dpn_prop = slave->prop.sink_dpn_prop;
+	}
+
+	for (i = 0; i < num_ports; i++) {
+		dpn_prop = &dpn_prop[i];
+
+		if (dpn_prop->num == port_num)
+			return &dpn_prop[i];
+	}
+
+	return NULL;
+}
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 228fdbf506fe..f2b952148e1a 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -367,7 +367,30 @@  struct sdw_slave_intr_status {
 };
 
 /**
- * struct sdw_slave_ops - Slave driver callback ops
+ * sdw_reg_bank - SoundWire register banks
+ * @SDW_BANK0: Soundwire register bank 0
+ * @SDW_BANK1: Soundwire register bank 1
+ */
+enum sdw_reg_bank {
+	SDW_BANK0,
+	SDW_BANK1,
+};
+
+/**
+ * struct sdw_bus_params: Structure holding bus configuration
+ *
+ * @curr_bank: Current bank in use (BANK0/BANK1)
+ * @next_bank: Next bank to use (BANK0/BANK1). next_bank will always be
+ * set to !curr_bank
+ */
+struct sdw_bus_params {
+	enum sdw_reg_bank curr_bank;
+	enum sdw_reg_bank next_bank;
+};
+
+/**
+ * struct sdw_slave_ops: Slave driver callback ops
+ *
  * @read_prop: Read Slave properties
  * @interrupt_callback: Device interrupt notification (invoked in thread
  * context)
@@ -482,6 +505,24 @@  struct sdw_transport_params {
 	unsigned int lane_ctrl;
 };
 
+/**
+ * struct sdw_master_port_ops: Callback functions from bus to Master
+ * driver to set Master Data ports.
+ *
+ * @dpn_set_port_params: Set the Port parameters for the Master Port.
+ * Mandatory callback
+ * @dpn_set_port_transport_params: Set transport parameters for the Master
+ * Port. Mandatory callback
+ */
+struct sdw_master_port_ops {
+	int (*dpn_set_port_params)(struct sdw_bus *bus,
+			struct sdw_port_params *port_params,
+			unsigned int bank);
+	int (*dpn_set_port_transport_params)(struct sdw_bus *bus,
+			struct sdw_transport_params *transport_params,
+			enum sdw_reg_bank bank);
+};
+
 struct sdw_msg;
 
 /**
@@ -525,6 +566,8 @@  struct sdw_master_ops {
  * @bus_lock: bus lock
  * @msg_lock: message lock
  * @ops: Master callback ops
+ * @port_ops: Master port callback ops
+ * @params: Current bus parameters
  * @prop: Master properties
  * @m_rt_list: List of Master instance of all stream(s) running on Bus. This
  * is used to compute and program bus bandwidth, clock, frame shape,
@@ -540,6 +583,8 @@  struct sdw_bus {
 	struct mutex bus_lock;
 	struct mutex msg_lock;
 	const struct sdw_master_ops *ops;
+	const struct sdw_master_port_ops *port_ops;
+	struct sdw_bus_params params;
 	struct sdw_master_prop prop;
 	struct list_head m_rt_list;
 	struct sdw_defer defer_msg;