This API is used by:
        1. Bus driver to read/write MIPI defined registers.
        2. Slave driver to read/write SoundWire Slave implementation
        defined registers. Slave driver should use regmap driver to
        access implementation defined registers, regmap driver uses
        read/write APIs internally.

Signed-off-by: Hardik Shah <[email protected]>
Signed-off-by: Sanyog Kale <[email protected]>
Reviewed-by: Pierre-Louis Bossart <[email protected]>
---
 sound/sdw/sdw.c      |  188 +++++++++++++++++++++++++++++++++++++++++++++++++-
 sound/sdw/sdw_priv.h |  144 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 329 insertions(+), 3 deletions(-)

diff --git a/sound/sdw/sdw.c b/sound/sdw/sdw.c
index d4e79b8a..c98e4d7 100644
--- a/sound/sdw/sdw.c
+++ b/sound/sdw/sdw.c
@@ -335,9 +335,191 @@ static int sdw_match(struct device *dev, struct 
device_driver *driver)
 };
 
 /**
- * snd_sdw_master_register_driver: SoundWire Master driver registration with
- *     bus. This API will register the Master driver with the SoundWire
- *     bus. It is typically called from the driver's module-init function.
+ * sdw_transfer: Local function where logic is placed to handle NOPM and PM
+ *     variants of the Slave transfer functions.
+ *
+ * @mstr: Handle to SDW Master
+ * @msg: One or more messages to be transferred
+ * @num: Number of messages to be transferred.
+ *
+ * Returns negative error, else the number of messages transferred.
+ *
+ */
+static int sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num,
+                               struct sdw_deferred_xfer_data *data)
+{
+       unsigned long orig_jiffies;
+       int ret, try, i;
+       int program_scp_addr_page = false;
+       u8 prev_adr_pg1 = 0;
+       u8 prev_adr_pg2 = 0;
+
+       for (i = 0; i < num; i++) {
+
+               /* Reset timeout for every message */
+               orig_jiffies = jiffies;
+
+               /* Inform Master driver to program SCP addr or not */
+               if ((prev_adr_pg1 != msg[i].addr_page1) ||
+                       (prev_adr_pg2 != msg[i].addr_page2))
+                       program_scp_addr_page = true;
+
+               for (ret = 0, try = 0; try <= mstr->retries; try++) {
+
+                       /* Call deferred or sync handler based on call */
+                       if (!data)
+                               ret = mstr->driver->ops->xfer_msg(mstr,
+                                       &msg[i], program_scp_addr_page);
+
+                       else if (mstr->driver->ops->xfer_msg_deferred)
+                               mstr->driver->ops->xfer_msg_deferred(
+                                               mstr, &msg[i],
+                                               program_scp_addr_page,
+                                               data);
+                       else
+                               return -ENOTSUPP;
+                       if (ret != -EAGAIN)
+                               break;
+
+                       if (time_after(jiffies, orig_jiffies + mstr->timeout))
+                               break;
+               }
+
+
+               /*
+                * Set previous address page as current once message is
+                * transferred.
+                */
+               prev_adr_pg1 = msg[i].addr_page1;
+               prev_adr_pg2 = msg[i].addr_page2;
+       }
+
+       orig_jiffies = jiffies;
+
+       ret = 0;
+
+       /* Reset page address if its other than 0 */
+       if (msg[i].addr_page1 && msg[i].addr_page2) {
+               for (try = 0; try <= mstr->retries; try++) {
+                       /*
+                        * Reset the page address to 0, so that always there
+                        * is fast path access to MIPI defined Slave
+                        * registers.
+                        */
+
+                       ret = mstr->driver->ops->reset_page_addr(
+                                       mstr, msg[0].dev_num);
+
+                       if (ret != -EAGAIN)
+                               break;
+
+                       if (time_after(jiffies, orig_jiffies + mstr->timeout))
+                               break;
+               }
+       }
+
+       if (!ret)
+               return i + 1;
+
+       return ret;
+}
+
+/**
+ * sdw_bank_switch_deferred: Initiate the transfer of the message but
+ *     doesn't wait for the message to be completed. Bus driver waits
+ *     outside context of this API for master driver to signal message
+ *     transfer complete. This is not Public API, this is used by Bus
+ *     driver only for Bank switch.
+ *
+ * @mstr: Master which will transfer the message.
+ * @msg: Message to be transferred. Message length of only 1 is supported.
+ * @data: Deferred information for the message to be transferred. This is
+ *     filled by Master on message transfer complete.
+ *
+ * Returns immediately after initiating the transfer, Bus driver needs to
+ * wait on xfer_complete, part of data, which is set by Master driver on
+ * completion of message transfer.
+ *
+ */
+void sdw_bank_switch_deferred(struct sdw_master *mstr, struct sdw_msg *msg,
+                               struct sdw_deferred_xfer_data *data)
+{
+
+       pm_runtime_get_sync(&mstr->dev);
+
+       sdw_transfer(mstr, msg, 1, data);
+
+       pm_runtime_mark_last_busy(&mstr->dev);
+       pm_runtime_put_sync_autosuspend(&mstr->dev);
+
+}
+
+/**
+ * snd_sdw_slave_transfer: Transfer message on bus.
+ *
+ * @master: Master which will transfer the message.
+ * @msg: Array of messages to be transferred.
+ * @num: Number of messages to be transferred, messages include read and
+ *     write messages, but not the ping commands. The read and write
+ *     messages are transmitted as a part of read and write SoundWire
+ *     commands with a parameter containing the payload.
+ *
+ * Returns the number of messages successfully transferred else appropriate
+ * error code.
+ */
+int snd_sdw_slave_transfer(struct sdw_master *master, struct sdw_msg *msg,
+                                                       unsigned int num)
+{
+       int ret;
+
+       /*
+        * Master reports the successfully transmitted messages onto the
+        * bus. If there are N message to be transmitted onto bus, and if
+        * Master gets error at (N-2) message it will report number of
+        * message transferred as N-2 Error is reported if ACK is not
+        * received for all messages or NACK is received for any of the
+        * transmitted messages. Currently both ACK not getting received
+        * and NACK is treated as error. But for upper level like regmap,
+        * both (Absence of ACK or NACK) errors are same as failure.
+        */
+
+       /*
+        * Make sure Master is woken up before message transfer Ideally
+        * function calling this should have wokenup Master as this will be
+        * called by Slave driver, and it will do runtime_get for itself,
+        * which will make sure Master is woken up as Master is parent Linux
+        * device of Slave. But if Slave is not implementing RTPM, it may
+        * not do this, so bus driver has to do it always irrespective of
+        * what Slave does.
+        */
+       pm_runtime_get_sync(&master->dev);
+
+       if (in_atomic() || irqs_disabled()) {
+               ret = mutex_trylock(&master->msg_lock);
+               if (!ret) {
+                       ret = -EAGAIN;
+                       goto out;
+               }
+       } else {
+               mutex_lock(&master->msg_lock);
+       }
+
+       ret = sdw_transfer(master, msg, num, NULL);
+
+       mutex_unlock(&master->msg_lock);
+out:
+       /* Put Master to sleep once message is transferred */
+       pm_runtime_mark_last_busy(&master->dev);
+       pm_runtime_put_sync_autosuspend(&master->dev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_sdw_slave_transfer);
+
+/**
+ * snd_sdw_master_register_driver: This API will register the Master driver
+ *     with the SoundWire bus. It is typically called from the driver's
+ *     module-init function.
  *
  * @driver: Master Driver to be associated with Master interface.
  * @owner: Module owner, generally THIS module.
diff --git a/sound/sdw/sdw_priv.h b/sound/sdw/sdw_priv.h
index 5911aa6..0af1c99 100644
--- a/sound/sdw/sdw_priv.h
+++ b/sound/sdw/sdw_priv.h
@@ -99,4 +99,148 @@ struct snd_sdw_core {
        struct idr idr;
 };
 
+/**
+ * sdw_bank_switch_deferred: Initiate the transfer of the message but
+ *     doesn't wait for the message to be completed. Bus driver waits
+ *     outside context of this API for master driver to signal message
+ *     transfer complete. This is not Public API, this is used by Bus
+ *     driver only for Bank switch.
+ *
+ * @mstr: Master which will transfer the message.
+ * @msg: Message to be transferred. Message length of only 1 is supported.
+ * @data: Deferred information for the message to be transferred. This is
+ *     filled by Master on message transfer complete.
+ *
+ * Returns immediately after initiating the transfer, Bus driver needs to
+ * wait on xfer_complete, part of data, which is set by Master driver on
+ * completion of message transfer.
+ */
+void sdw_bank_switch_deferred(struct sdw_master *mstr, struct sdw_msg *msg,
+                               struct sdw_deferred_xfer_data *data);
+/*
+ * Helper function for bus driver to write messages. Since bus driver
+ * operates on MIPI defined Slave registers, addr_page1 and addr_page2 is
+ * set to 0.
+ */
+static inline int sdw_wr_msg(struct sdw_msg *msg, bool xmit_on_ssp, u16 addr,
+                                       u16 len, u8 *buf, u8 dev_num,
+                                       struct sdw_master *mstr,
+                                       int num_msg)
+{
+       msg->xmit_on_ssp = xmit_on_ssp;
+       msg->r_w_flag = SDW_MSG_FLAG_WRITE;
+       msg->addr = addr;
+       msg->len = len;
+       msg->buf = buf;
+       msg->dev_num = dev_num;
+       msg->addr_page1 = 0x0;
+       msg->addr_page2 = 0x0;
+
+       return snd_sdw_slave_transfer(mstr, msg, num_msg);
+}
+
+/*
+ * Helper function for bus driver to read messages. Since bus driver
+ * operates on MIPI defined Slave registers, addr_page1 and addr_page2 is
+ * set to 0.
+ */
+static inline int sdw_rd_msg(struct sdw_msg *msg, bool xmit_on_ssp, u16 addr,
+                                       u16 len, u8 *buf, u8 dev_num,
+                                       struct sdw_master *mstr,
+                                       int num_msg)
+{
+       msg->xmit_on_ssp = xmit_on_ssp;
+       msg->r_w_flag = SDW_MSG_FLAG_READ;
+       msg->addr = addr;
+       msg->len = len;
+       msg->buf = buf;
+       msg->dev_num = dev_num;
+       msg->addr_page1 = 0x0;
+       msg->addr_page2 = 0x0;
+
+       return snd_sdw_slave_transfer(mstr, msg, num_msg);
+}
+
+/*
+ * Helper function for bus driver to write messages (nopm version). Since
+ * bus driver operates on MIPI defined Slave registers, addr_page1 and
+ * addr_page2 is set to 0.
+ */
+static inline int sdw_wr_msg_nopm(struct sdw_msg *msg, bool xmit_on_ssp,
+                                       u16 addr, u16 len, u8 *buf,
+                                       u8 dev_num,
+                                       struct sdw_master *mstr,
+                                       int num_msg)
+{
+       msg->xmit_on_ssp = xmit_on_ssp;
+       msg->r_w_flag = SDW_MSG_FLAG_WRITE;
+       msg->addr = addr;
+       msg->len = len;
+       msg->buf = buf;
+       msg->dev_num = dev_num;
+       msg->addr_page1 = 0x0;
+       msg->addr_page2 = 0x0;
+
+       return snd_sdw_slave_transfer(mstr, msg, num_msg);
+}
+
+/*
+ * Helper function for bus driver to read messages (nopm version). Since
+ * bus driver operates on MIPI defined Slave registers, addr_page1 and
+ * addr_page2 is set to 0.
+ */
+static inline int sdw_rd_msg_nopm(struct sdw_msg *msg, bool xmit_on_ssp,
+                                       u16 addr, u16 len, u8 *buf,
+                                       u8 dev_num,
+                                       struct sdw_master *mstr,
+                                       int num_msg)
+{
+       msg->xmit_on_ssp = xmit_on_ssp;
+       msg->r_w_flag = SDW_MSG_FLAG_READ;
+       msg->addr = addr;
+       msg->len = len;
+       msg->buf = buf;
+       msg->dev_num = dev_num;
+       msg->addr_page1 = 0x0;
+       msg->addr_page2 = 0x0;
+
+       return snd_sdw_slave_transfer(mstr, msg, num_msg);
+}
+
+/*
+ * Helper function for bus driver to create read messages. Since bus driver
+ * operates on MIPI defined Slave registers, addr_page1 and addr_page2 is
+ * set to 0.
+ */
+static inline void sdw_create_rd_msg(struct sdw_msg *msg, bool xmit_on_ssp,
+                               u16 addr, u16 len, u8 *buf, u8 dev_num)
+{
+       msg->xmit_on_ssp = xmit_on_ssp;
+       msg->r_w_flag = SDW_MSG_FLAG_READ;
+       msg->addr = addr;
+       msg->len = len;
+       msg->buf = buf;
+       msg->dev_num = dev_num;
+       msg->addr_page1 = 0x0;
+       msg->addr_page2 = 0x0;
+}
+
+/*
+ * Helper function for bus driver to create write messages. Since bus driver
+ * operates on MIPI defined Slave registers, addr_page1 and addr_page2 is
+ * set to 0.
+ */
+static inline void sdw_create_wr_msg(struct sdw_msg *msg, bool xmit_on_ssp,
+                               u16 addr, u16 len, u8 *buf, u8 dev_num)
+{
+       msg->xmit_on_ssp = xmit_on_ssp;
+       msg->r_w_flag = SDW_MSG_FLAG_WRITE;
+       msg->addr = addr;
+       msg->len = len;
+       msg->buf = buf;
+       msg->dev_num = dev_num;
+       msg->addr_page1 = 0x0;
+       msg->addr_page2 = 0x0;
+}
+
 #endif /* _LINUX_SDW_PRIV_H */
-- 
1.7.9.5

Reply via email to