Message ID | 1522946904-2089-5-git-send-email-vinod.koul@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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; >
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 --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;