|
@@ -15,6 +15,43 @@
|
|
|
#include <linux/soundwire/sdw.h>
|
|
|
#include "bus.h"
|
|
|
|
|
|
+/*
|
|
|
+ * Array of supported rows and columns as per MIPI SoundWire Specification 1.1
|
|
|
+ *
|
|
|
+ * The rows are arranged as per the array index value programmed
|
|
|
+ * in register. The index 15 has dummy value 0 in order to fill hole.
|
|
|
+ */
|
|
|
+int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147,
|
|
|
+ 96, 100, 120, 128, 150, 160, 250, 0,
|
|
|
+ 192, 200, 240, 256, 72, 144, 90, 180};
|
|
|
+
|
|
|
+int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
|
|
|
+
|
|
|
+static int sdw_find_col_index(int col)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < SDW_FRAME_COLS; i++) {
|
|
|
+ if (cols[i] == col)
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_warn("Requested column not found, selecting lowest column no: 2\n");
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdw_find_row_index(int row)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < SDW_FRAME_ROWS; i++) {
|
|
|
+ if (rows[i] == row)
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_warn("Requested row not found, selecting lowest row no: 48\n");
|
|
|
+ return 0;
|
|
|
+}
|
|
|
static int _sdw_program_slave_port_params(struct sdw_bus *bus,
|
|
|
struct sdw_slave *slave,
|
|
|
struct sdw_transport_params *t_params,
|
|
@@ -514,6 +551,171 @@ static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * sdw_notify_config() - Notify bus configuration
|
|
|
+ *
|
|
|
+ * @m_rt: Master runtime handle
|
|
|
+ *
|
|
|
+ * This function notifies the Master(s) and Slave(s) of the
|
|
|
+ * new bus configuration.
|
|
|
+ */
|
|
|
+static int sdw_notify_config(struct sdw_master_runtime *m_rt)
|
|
|
+{
|
|
|
+ struct sdw_slave_runtime *s_rt;
|
|
|
+ struct sdw_bus *bus = m_rt->bus;
|
|
|
+ struct sdw_slave *slave;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (bus->ops->set_bus_conf) {
|
|
|
+ ret = bus->ops->set_bus_conf(bus, &bus->params);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
|
|
|
+ slave = s_rt->slave;
|
|
|
+
|
|
|
+ if (slave->ops->bus_config) {
|
|
|
+ ret = slave->ops->bus_config(slave, &bus->params);
|
|
|
+ if (ret < 0)
|
|
|
+ dev_err(bus->dev, "Notify Slave: %d failed",
|
|
|
+ slave->dev_num);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * sdw_program_params() - Program transport and port parameters for Master(s)
|
|
|
+ * and Slave(s)
|
|
|
+ *
|
|
|
+ * @bus: SDW bus instance
|
|
|
+ */
|
|
|
+static int sdw_program_params(struct sdw_bus *bus)
|
|
|
+{
|
|
|
+ struct sdw_master_runtime *m_rt = NULL;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
|
|
|
+ ret = sdw_program_port_params(m_rt);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev,
|
|
|
+ "Program transport params failed: %d", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = sdw_notify_config(m_rt);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "Notify bus config failed: %d", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Enable port(s) on alternate bank for all active streams */
|
|
|
+ if (m_rt->stream->state != SDW_STREAM_ENABLED)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = sdw_enable_disable_ports(m_rt, true);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "Enable channel failed: %d", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdw_bank_switch(struct sdw_bus *bus)
|
|
|
+{
|
|
|
+ int col_index, row_index;
|
|
|
+ struct sdw_msg *wr_msg;
|
|
|
+ u8 *wbuf = NULL;
|
|
|
+ int ret = 0;
|
|
|
+ u16 addr;
|
|
|
+
|
|
|
+ wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL);
|
|
|
+ if (!wr_msg)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
|
|
|
+ if (!wbuf) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto error_1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get row and column index to program register */
|
|
|
+ col_index = sdw_find_col_index(bus->params.col);
|
|
|
+ row_index = sdw_find_row_index(bus->params.row);
|
|
|
+ wbuf[0] = col_index | (row_index << 3);
|
|
|
+
|
|
|
+ if (bus->params.next_bank)
|
|
|
+ addr = SDW_SCP_FRAMECTRL_B1;
|
|
|
+ else
|
|
|
+ addr = SDW_SCP_FRAMECTRL_B0;
|
|
|
+
|
|
|
+ sdw_fill_msg(wr_msg, NULL, addr, 1, SDW_BROADCAST_DEV_NUM,
|
|
|
+ SDW_MSG_FLAG_WRITE, wbuf);
|
|
|
+ wr_msg->ssp_sync = true;
|
|
|
+
|
|
|
+ ret = sdw_transfer(bus, wr_msg);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "Slave frame_ctrl reg write failed");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree(wr_msg);
|
|
|
+ kfree(wbuf);
|
|
|
+ bus->defer_msg.msg = NULL;
|
|
|
+ bus->params.curr_bank = !bus->params.curr_bank;
|
|
|
+ bus->params.next_bank = !bus->params.next_bank;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ kfree(wbuf);
|
|
|
+error_1:
|
|
|
+ kfree(wr_msg);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int do_bank_switch(struct sdw_stream_runtime *stream)
|
|
|
+{
|
|
|
+ struct sdw_master_runtime *m_rt = stream->m_rt;
|
|
|
+ const struct sdw_master_ops *ops;
|
|
|
+ struct sdw_bus *bus = m_rt->bus;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ ops = bus->ops;
|
|
|
+
|
|
|
+ /* Pre-bank switch */
|
|
|
+ if (ops->pre_bank_switch) {
|
|
|
+ ret = ops->pre_bank_switch(bus);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "Pre bank switch op failed: %d", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Bank switch */
|
|
|
+ ret = sdw_bank_switch(bus);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "Bank switch failed: %d", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Post-bank switch */
|
|
|
+ if (ops->post_bank_switch) {
|
|
|
+ ret = ops->post_bank_switch(bus);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev,
|
|
|
+ "Post bank switch op failed: %d", ret);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* sdw_release_stream() - Free the assigned stream runtime
|
|
|
*
|