soundwire: Add Master and Slave port programming
authorSanyog Kale <sanyog.r.kale@intel.com>
Thu, 26 Apr 2018 13:08:17 +0000 (18:38 +0530)
committerVinod Koul <vkoul@kernel.org>
Fri, 11 May 2018 16:17:04 +0000 (21:47 +0530)
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: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/soundwire/bus.h
drivers/soundwire/stream.c
include/linux/soundwire/sdw.h

index 39e6811e435cb6280e5d2658b790ae7d6c5bbd9e..133268ab80860387514238f27cc1cb57aaf056d8 100644 (file)
@@ -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);
index 289342340262f9acfb6ff0e76c065528e05f44dd..ea6ee96b02d20b25818325df8dc1c5bcba28405b 100644 (file)
 #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;
+       u16 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);
+       wbuf &= SDW_DPN_SAMPLECTRL_HIGH;
+       wbuf >>= 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->hstart;
+       wbuf <<= SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART);
+       wbuf |= t_params->hstop;
+
+       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
  *
@@ -498,3 +727,36 @@ error:
        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;
+}
index 5e06a0de508c8129c8a2975585e4c93de60f32ba..03b55804d830398a1f0d3170b533a7f127905fea 100644 (file)
@@ -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;