mbox series

[RFC,0/6] soundwire: Add support to Qualcomm SoundWire master

Message ID 20190607085643.932-1-srinivas.kandagatla@linaro.org
Headers show
Series soundwire: Add support to Qualcomm SoundWire master | expand

Message

Srinivas Kandagatla June 7, 2019, 8:56 a.m. UTC
Hi All, 

This patchset is very first version of Qualcomm SoundWire Master Controller
found in most of Qualcomm SoCs and WCD audio codecs.

This driver along with WCD934x codec and WSA881x Class-D Smart Speaker Amplifier
drivers is on DragonBoard DB845c based of SDM845 SoC.
WCD934x and WSA881x patches will be posted soon.

SoundWire controller on SDM845 is integrated in WCD934x audio codec via
SlimBus interface.

Currently this driver is very minimal and only supports PDM.

Most of the code in this driver is rework of Qualcomm downstream drivers
used in Andriod. Credits to Banajit Goswami and Patrick Lai's Team.

TODO:
	Test and add PCM support.

Thanks,
srini

Srinivas Kandagatla (5):
  ASoC: core: add support to snd_soc_dai_get_sdw_stream()
  soundwire: core: define SDW_MAX_PORT
  soundwire: stream: make stream name a const pointer
  dt-bindings: soundwire: add bindings for Qcom controller
  soundwire: qcom: add support for SoundWire controller

Vinod Koul (1):
  soundwire: Add compute_params callback

 .../bindings/soundwire/qcom,swr.txt           |  62 ++
 drivers/soundwire/Kconfig                     |   9 +
 drivers/soundwire/Makefile                    |   4 +
 drivers/soundwire/qcom.c                      | 983 ++++++++++++++++++
 drivers/soundwire/stream.c                    |  11 +-
 include/linux/soundwire/sdw.h                 |   7 +-
 include/sound/soc-dai.h                       |  10 +
 7 files changed, 1083 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/soundwire/qcom,swr.txt
 create mode 100644 drivers/soundwire/qcom.c

-- 
2.21.0

Comments

Vinod Koul June 10, 2019, 6:40 a.m. UTC | #1
On 07-06-19, 09:56, Srinivas Kandagatla wrote:
> Qualcomm SoundWire Master controller is present in most Qualcomm SoCs

> either integrated as part of WCD audio codecs via slimbus or

> as part of SOC I/O.

> 

> This patchset adds support to a very basic controller which has been

> tested with WCD934x SoundWire controller connected to WSA881x smart

> speaker amplifiers.

> 

> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

> ---

>  drivers/soundwire/Kconfig  |   9 +

>  drivers/soundwire/Makefile |   4 +

>  drivers/soundwire/qcom.c   | 983 +++++++++++++++++++++++++++++++++++++


Can you split this to two patches (at least), master driver followed by
DAI driver

> +

> +#define SWRM_COMP_HW_VERSION					0x00


What does COMP stand for?

> +#define SWRM_COMP_CFG_ADDR					0x04

> +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK			BIT(1)

> +#define SWRM_COMP_CFG_ENABLE_MSK				BIT(0)

> +#define SWRM_COMP_PARAMS					0x100

> +#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK			GENMASK(4, 0)

> +#define SWRM_COMP_PARAMS_DIN_PORTS_MASK				GENMASK(9, 5)

> +#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH				GENMASK(14, 10)

> +#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH				GENMASK(19, 15)

> +#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES			GENMASK(32. 20)


Thats a lot of text, So as others have said we need to rethink the
naming conventions, perhaps QC_SDW_PARAM_AUTO_ENUM (feel free to drop
SDW as well from here as it QC specific!

Also move common ones to core..

> +#define SWRM_INTERRUPT_STATUS					0x200

> +#define SWRM_INTERRUPT_STATUS_RMSK				GENMASK(16, 0)

> +#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ			BIT(0)

> +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED		BIT(1)

> +#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS		BIT(2)

> +#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET			BIT(3)

> +#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW			BIT(4)

> +#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW			BIT(5)

> +#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW		BIT(6)

> +#define SWRM_INTERRUPT_STATUS_CMD_ERROR				BIT(7)

> +#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION		BIT(8)

> +#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH		BIT(9)

> +#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED		BIT(10)

> +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED	BIT(11)

> +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED			BIT(12)

> +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL		BIT(13)

> +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED		BIT(14)

> +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED			BIT(15)

> +#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST			BIT(16)

> +#define SWRM_INTERRUPT_MASK_ADDR				0x204

> +#define SWRM_INTERRUPT_CLEAR					0x208

> +#define SWRM_CMD_FIFO_WR_CMD					0x300

> +#define SWRM_CMD_FIFO_RD_CMD					0x304

> +#define SWRM_CMD_FIFO_CMD					0x308

> +#define SWRM_CMD_FIFO_STATUS					0x30C

> +#define SWRM_CMD_FIFO_CFG_ADDR					0x314

> +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT			0x0

> +#define SWRM_CMD_FIFO_RD_FIFO_ADDR				0x318

> +#define SWRM_ENUMERATOR_CFG_ADDR				0x500

> +#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m)		(0x101C + 0x40 * (m))

> +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT		16

> +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT			3

> +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK			GENMASK(2, 0)

> +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT			0

> +#define SWRM_MCP_CFG_ADDR					0x1048

> +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK		GENMASK(21, 17)

> +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT		0x11

> +#define SWRM_MCP_STATUS						0x104C

> +#define SWRM_MCP_STATUS_BANK_NUM_MASK				BIT(0)

> +#define SWRM_MCP_SLV_STATUS					0x1090

> +#define SWRM_MCP_SLV_STATUS_MASK				GENMASK(1, 0)

> +#define SWRM_DP_PORT_CTRL_BANK(n, m)	(0x1124 + 0x100 * (n - 1) + 0x40 * m)

> +#define SWRM_DP_PORT_CTRL2_BANK(n, m)	(0x1126 + 0x100 * (n - 1) + 0x40 * m)

> +#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT				0x18

> +#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT				0x10

> +#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT				0x08

> +#define SWRM_AHB_BRIDGE_WR_DATA_0				0xc885

> +#define SWRM_AHB_BRIDGE_WR_ADDR_0				0xc889

> +#define SWRM_AHB_BRIDGE_RD_ADDR_0				0xc88d

> +#define SWRM_AHB_BRIDGE_RD_DATA_0				0xc891

> +

> +#define SWRM_REG_VAL_PACK(data, dev, id, reg)	\

> +			((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))

> +

> +#define SWRM_MAX_ROW_VAL	0 /* Rows = 48 */

> +#define SWRM_DEFAULT_ROWS	48

> +#define SWRM_MIN_COL_VAL	0 /* Cols = 2 */

> +#define SWRM_DEFAULT_COL	16

> +#define SWRM_SPECIAL_CMD_ID	0xF

> +#define MAX_FREQ_NUM		1

> +#define TIMEOUT_MS		1000

> +#define QCOM_SWRM_MAX_RD_LEN	0xf

> +#define DEFAULT_CLK_FREQ	9600000

> +#define SWRM_MAX_DAIS		0xF


I was thinking it would make sense to move this to DT, DAI is after all
a hw property!

> +static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data,

> +				     u8 dev_addr, u16 reg_addr)

> +{

> +	int ret = 0;

> +	u8 cmd_id;

> +	u32 val;

> +

> +	mutex_lock(&ctrl->lock);

> +	if (dev_addr == SDW_BROADCAST_DEV_NUM) {

> +		cmd_id = SWRM_SPECIAL_CMD_ID;

> +	} else {

> +		if (++ctrl->wr_cmd_id == SWRM_SPECIAL_CMD_ID)

> +			ctrl->wr_cmd_id = 0;

> +

> +		cmd_id = ctrl->wr_cmd_id;

> +	}

> +

> +	val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, cmd_id, reg_addr);

> +	ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_WR_CMD, val);

> +	if (ret < 0) {

> +		dev_err(ctrl->dev, "%s: reg 0x%x write failed, err:%d\n",

> +			__func__, val, ret);

> +		goto err;

> +	}

> +

> +	if (dev_addr == SDW_BROADCAST_DEV_NUM) {

> +		ctrl->fifo_status = 0;

> +		ret = wait_for_completion_timeout(&ctrl->sp_cmd_comp,

> +						  msecs_to_jiffies(TIMEOUT_MS));


why not wait for completion on non broadcast?

> +static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *ctrl,

> +				     u8 dev_addr, u16 reg_addr,

> +				     u32 len, u8 *rval)

> +{

> +	int i, ret = 0;


Superfluous initialization of ret

> +static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)

> +{

> +	struct qcom_swrm_ctrl *ctrl = dev_id;

> +	u32 sts, value;

> +

> +	sts = ctrl->reg_read(ctrl, SWRM_INTERRUPT_STATUS);

> +

> +	if (sts & SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED)

> +		complete(&ctrl->sp_cmd_comp);

> +

> +	if (sts & SWRM_INTERRUPT_STATUS_CMD_ERROR) {

> +		value = ctrl->reg_read(ctrl, SWRM_CMD_FIFO_STATUS);

> +		dev_err_ratelimited(ctrl->dev,

> +				    "CMD error, fifo status 0x%x\n",

> +				     value);

> +		ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1);

> +		if ((value & 0xF) == 0xF) {

> +			ctrl->fifo_status = -ENODATA;

> +			complete(&ctrl->sp_cmd_comp);

> +		}

> +	}

> +

> +	if ((sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED) ||

> +	    sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS) {

> +		if (sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED)

> +			ctrl->status[0] = SDW_SLAVE_ATTACHED;

> +

> +		schedule_work(&ctrl->slave_work);


So why are we scheduling work, you are the thread handler so I think it
should be okay and better to invoke bus for status update.

> +	}

> +

> +	if (sts & SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ)

> +		dev_dbg(ctrl->dev, "Slave pend irq\n");

> +

> +	if (sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED)

> +		dev_dbg(ctrl->dev, "New slave attached\n");


No updating bus on the status?

> +static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,

> +						    struct sdw_msg *msg)

> +{

> +	struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);

> +	int ret, i, len;

> +

> +	if (msg->flags == SDW_MSG_FLAG_READ) {

> +		for (i = 0; i < msg->len;) {

> +			if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN)

> +				len = msg->len - i;

> +			else

> +				len = QCOM_SWRM_MAX_RD_LEN;

> +

> +			ret = qcom_swrm_cmd_fifo_rd_cmd(ctrl, msg->dev_num,

> +							msg->addr + i, len,

> +						       &msg->buf[i]);

> +			if (ret < 0) {

> +				if (ret == -ENODATA)

> +					return SDW_CMD_IGNORED;

> +

> +				return ret;

> +			}

> +

> +			i = i + len;

> +		}

> +	} else if (msg->flags == SDW_MSG_FLAG_WRITE) {

> +		for (i = 0; i < msg->len; i++) {

> +			ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->buf[i],

> +							msg->dev_num,

> +						       msg->addr + i);

> +			if (ret < 0) {

> +				if (ret == -ENODATA)

> +					return SDW_CMD_IGNORED;

> +

> +				return ret;


So we need to convert this to sdw_command_response before returning.

> +static int qcom_swrm_prepare(struct snd_pcm_substream *substream,

> +			     struct snd_soc_dai *dai)

> +{

> +	struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);

> +

> +	if (!ctrl->sruntime[dai->id])

> +		return -EINVAL;

> +

> +	return sdw_enable_stream(ctrl->sruntime[dai->id]);


Hmm you need to handle dai prepare being called for multiple times.

> +static int qcom_pdm_set_sdw_stream(struct snd_soc_dai *dai,

> +				   void *stream, int direction)

> +{

> +	return 0;

> +}


lets remove if we dont intend to do anything!

> +static int qcom_swrm_runtime_suspend(struct device *device)

> +{

> +	/* TBD */

> +	return 0;

> +}

> +

> +static int qcom_swrm_runtime_resume(struct device *device)

> +{

> +	/* TBD */

> +	return 0;

> +}


Again, lets remove these, add when we have the functionality
-- 
~Vinod
Srinivas Kandagatla June 10, 2019, 8:27 a.m. UTC | #2
Thanks for taking time to review!


On 10/06/2019 07:40, Vinod Koul wrote:
> On 07-06-19, 09:56, Srinivas Kandagatla wrote:

>> Qualcomm SoundWire Master controller is present in most Qualcomm SoCs

>> either integrated as part of WCD audio codecs via slimbus or

>> as part of SOC I/O.

>>

>> This patchset adds support to a very basic controller which has been

>> tested with WCD934x SoundWire controller connected to WSA881x smart

>> speaker amplifiers.

>>

>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

>> ---

>>   drivers/soundwire/Kconfig  |   9 +

>>   drivers/soundwire/Makefile |   4 +

>>   drivers/soundwire/qcom.c   | 983 +++++++++++++++++++++++++++++++++++++

> 

> Can you split this to two patches (at least), master driver followed by

> DAI driver


Sure.

> 

>> +

>> +#define SWRM_COMP_HW_VERSION					0x00

> 

> What does COMP stand for?


Controller splits registers into "Component(core configuration 
registers)", "Interrupt" "Command Fifo" and so on...

> 

>> +#define SWRM_COMP_CFG_ADDR					0x04

>> +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK			BIT(1)

>> +#define SWRM_COMP_CFG_ENABLE_MSK				BIT(0)

>> +#define SWRM_COMP_PARAMS					0x100

>> +#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK			GENMASK(4, 0)

>> +#define SWRM_COMP_PARAMS_DIN_PORTS_MASK				GENMASK(9, 5)

>> +#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH				GENMASK(14, 10)

>> +#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH				GENMASK(19, 15)

>> +#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES			GENMASK(32. 20)

> 

> Thats a lot of text, So as others have said we need to rethink the

> naming conventions, perhaps QC_SDW_PARAM_AUTO_ENUM (feel free to drop

> SDW as well from here as it QC specific!

> 

> Also move common ones to core..

> 

>> +#define SWRM_INTERRUPT_STATUS					0x200

>> +#define SWRM_INTERRUPT_STATUS_RMSK				GENMASK(16, 0)

>> +#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ			BIT(0)

>> +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED		BIT(1)

>> +#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS		BIT(2)

>> +#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET			BIT(3)

>> +#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW			BIT(4)

>> +#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW			BIT(5)

>> +#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW		BIT(6)

>> +#define SWRM_INTERRUPT_STATUS_CMD_ERROR				BIT(7)

>> +#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION		BIT(8)

>> +#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH		BIT(9)

>> +#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED		BIT(10)

>> +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED	BIT(11)

>> +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED			BIT(12)

>> +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL		BIT(13)

>> +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED		BIT(14)

>> +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED			BIT(15)

>> +#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST			BIT(16)

>> +#define SWRM_INTERRUPT_MASK_ADDR				0x204

>> +#define SWRM_INTERRUPT_CLEAR					0x208

>> +#define SWRM_CMD_FIFO_WR_CMD					0x300

>> +#define SWRM_CMD_FIFO_RD_CMD					0x304

>> +#define SWRM_CMD_FIFO_CMD					0x308

>> +#define SWRM_CMD_FIFO_STATUS					0x30C

>> +#define SWRM_CMD_FIFO_CFG_ADDR					0x314

>> +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT			0x0

>> +#define SWRM_CMD_FIFO_RD_FIFO_ADDR				0x318

>> +#define SWRM_ENUMERATOR_CFG_ADDR				0x500

>> +#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m)		(0x101C + 0x40 * (m))

>> +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT		16

>> +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT			3

>> +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK			GENMASK(2, 0)

>> +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT			0

>> +#define SWRM_MCP_CFG_ADDR					0x1048

>> +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK		GENMASK(21, 17)

>> +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT		0x11

>> +#define SWRM_MCP_STATUS						0x104C

>> +#define SWRM_MCP_STATUS_BANK_NUM_MASK				BIT(0)

>> +#define SWRM_MCP_SLV_STATUS					0x1090

>> +#define SWRM_MCP_SLV_STATUS_MASK				GENMASK(1, 0)

>> +#define SWRM_DP_PORT_CTRL_BANK(n, m)	(0x1124 + 0x100 * (n - 1) + 0x40 * m)

>> +#define SWRM_DP_PORT_CTRL2_BANK(n, m)	(0x1126 + 0x100 * (n - 1) + 0x40 * m)

>> +#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT				0x18

>> +#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT				0x10

>> +#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT				0x08

>> +#define SWRM_AHB_BRIDGE_WR_DATA_0				0xc885

>> +#define SWRM_AHB_BRIDGE_WR_ADDR_0				0xc889

>> +#define SWRM_AHB_BRIDGE_RD_ADDR_0				0xc88d

>> +#define SWRM_AHB_BRIDGE_RD_DATA_0				0xc891

>> +

>> +#define SWRM_REG_VAL_PACK(data, dev, id, reg)	\

>> +			((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))

>> +

>> +#define SWRM_MAX_ROW_VAL	0 /* Rows = 48 */

>> +#define SWRM_DEFAULT_ROWS	48

>> +#define SWRM_MIN_COL_VAL	0 /* Cols = 2 */

>> +#define SWRM_DEFAULT_COL	16

>> +#define SWRM_SPECIAL_CMD_ID	0xF

>> +#define MAX_FREQ_NUM		1

>> +#define TIMEOUT_MS		1000

>> +#define QCOM_SWRM_MAX_RD_LEN	0xf

>> +#define DEFAULT_CLK_FREQ	9600000

>> +#define SWRM_MAX_DAIS		0xF

> 

> I was thinking it would make sense to move this to DT, DAI is after all

> a hw property!


I will give that a try before sending out next version.
> 

>> +static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data,

>> +				     u8 dev_addr, u16 reg_addr)

>> +{

>> +	int ret = 0;

>> +	u8 cmd_id;

>> +	u32 val;

>> +

>> +	mutex_lock(&ctrl->lock);

>> +	if (dev_addr == SDW_BROADCAST_DEV_NUM) {

>> +		cmd_id = SWRM_SPECIAL_CMD_ID;

>> +	} else {

>> +		if (++ctrl->wr_cmd_id == SWRM_SPECIAL_CMD_ID)

>> +			ctrl->wr_cmd_id = 0;

>> +

>> +		cmd_id = ctrl->wr_cmd_id;

>> +	}

>> +

>> +	val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, cmd_id, reg_addr);

>> +	ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_WR_CMD, val);

>> +	if (ret < 0) {

>> +		dev_err(ctrl->dev, "%s: reg 0x%x write failed, err:%d\n",

>> +			__func__, val, ret);

>> +		goto err;

>> +	}

>> +

>> +	if (dev_addr == SDW_BROADCAST_DEV_NUM) {

>> +		ctrl->fifo_status = 0;

>> +		ret = wait_for_completion_timeout(&ctrl->sp_cmd_comp,

>> +						  msecs_to_jiffies(TIMEOUT_MS));

> 

> why not wait for completion on non broadcast?

> 


This could lead to dead-lock if we endup reading registers from 
interrupt handler.

>> +static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *ctrl,

>> +				     u8 dev_addr, u16 reg_addr,

>> +				     u32 len, u8 *rval)

>> +{

>> +	int i, ret = 0;

> 

> Superfluous initialization of ret

> 

I agree.
>> +static irqreturn_t qcom_swrm_irq_handler(int irq, void *dev_id)

>> +{

>> +	struct qcom_swrm_ctrl *ctrl = dev_id;

>> +	u32 sts, value;

>> +

>> +	sts = ctrl->reg_read(ctrl, SWRM_INTERRUPT_STATUS);

>> +

>> +	if (sts & SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED)

>> +		complete(&ctrl->sp_cmd_comp);

>> +

>> +	if (sts & SWRM_INTERRUPT_STATUS_CMD_ERROR) {

>> +		value = ctrl->reg_read(ctrl, SWRM_CMD_FIFO_STATUS);

>> +		dev_err_ratelimited(ctrl->dev,

>> +				    "CMD error, fifo status 0x%x\n",

>> +				     value);

>> +		ctrl->reg_write(ctrl, SWRM_CMD_FIFO_CMD, 0x1);

>> +		if ((value & 0xF) == 0xF) {

>> +			ctrl->fifo_status = -ENODATA;

>> +			complete(&ctrl->sp_cmd_comp);

>> +		}

>> +	}

>> +

>> +	if ((sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED) ||

>> +	    sts & SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS) {

>> +		if (sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED)

>> +			ctrl->status[0] = SDW_SLAVE_ATTACHED;

>> +

>> +		schedule_work(&ctrl->slave_work);

> 

> So why are we scheduling work, you are the thread handler so I think it

> should be okay and better to invoke bus for status update.


I had seen lockup issues as this would trigger broadcast messages which 
would wait on completion interrupt!

> 

>> +	}

>> +

>> +	if (sts & SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ)

>> +		dev_dbg(ctrl->dev, "Slave pend irq\n");

>> +

>> +	if (sts & SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED)

>> +		dev_dbg(ctrl->dev, "New slave attached\n");

> 

> No updating bus on the status?

> 

Its done down below this function! These are debug messages only!
looks redundant, will remove it.

>> +static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,

>> +						    struct sdw_msg *msg)

>> +{

>> +	struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);

>> +	int ret, i, len;

>> +

>> +	if (msg->flags == SDW_MSG_FLAG_READ) {

>> +		for (i = 0; i < msg->len;) {

>> +			if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN)

>> +				len = msg->len - i;

>> +			else

>> +				len = QCOM_SWRM_MAX_RD_LEN;

>> +

>> +			ret = qcom_swrm_cmd_fifo_rd_cmd(ctrl, msg->dev_num,

>> +							msg->addr + i, len,

>> +						       &msg->buf[i]);

>> +			if (ret < 0) {

>> +				if (ret == -ENODATA)

>> +					return SDW_CMD_IGNORED;

>> +

>> +				return ret;

>> +			}

>> +

>> +			i = i + len;

>> +		}

>> +	} else if (msg->flags == SDW_MSG_FLAG_WRITE) {

>> +		for (i = 0; i < msg->len; i++) {

>> +			ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->buf[i],

>> +							msg->dev_num,

>> +						       msg->addr + i);

>> +			if (ret < 0) {

>> +				if (ret == -ENODATA)

>> +					return SDW_CMD_IGNORED;

>> +

>> +				return ret;

> 

> So we need to convert this to sdw_command_response before returning.

> 

Sure!

>> +static int qcom_swrm_prepare(struct snd_pcm_substream *substream,

>> +			     struct snd_soc_dai *dai)

>> +{

>> +	struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);

>> +

>> +	if (!ctrl->sruntime[dai->id])

>> +		return -EINVAL;

>> +

>> +	return sdw_enable_stream(ctrl->sruntime[dai->id]);

> 

> Hmm you need to handle dai prepare being called for multiple times.

> Thanks for pointing this out, Will fix this.


>> +static int qcom_pdm_set_sdw_stream(struct snd_soc_dai *dai,

>> +				   void *stream, int direction)

>> +{

>> +	return 0;

>> +}

> 

> lets remove if we dont intend to do anything!

> 

Hm, not sure how I missed this one!

>> +static int qcom_swrm_runtime_suspend(struct device *device)

>> +{

>> +	/* TBD */

>> +	return 0;

>> +}

>> +

>> +static int qcom_swrm_runtime_resume(struct device *device)

>> +{

>> +	/* TBD */

>> +	return 0;

>> +}

> 

> Again, lets remove these, add when we have the functionality

We have issues without this, as soundwire bus would return error on 
runtime pm get/set. For RFC, I had to make this dummy, I will be able to 
add and test some code in next 1/2 spins.

Thanks,
srini
>
Pierre-Louis Bossart June 10, 2019, 2:12 p.m. UTC | #3
>>> +#define SWRM_COMP_HW_VERSION                    0x00

>>

>> Can we please use SDW_ or QCOM_SDW_ as prefix?

>>

> 

> SWRM prefix is as per the data sheet register names, If it help am happy 

> to add QCOM_ prefix it.


That'd be fine. As long as there is no duplication and two 
terms/prefixes used for the same thing I am happy.

>>> +

>>> +    val = SWRM_REG_VAL_PACK(cmd_data, dev_addr, cmd_id, reg_addr);

>>> +    ret = ctrl->reg_write(ctrl, SWRM_CMD_FIFO_WR_CMD, val);

>>> +    if (ret < 0) {

>>> +        dev_err(ctrl->dev, "%s: reg 0x%x write failed, err:%d\n",

>>> +            __func__, val, ret);

>>> +        goto err;

>>> +    }

>>> +

>>> +    if (dev_addr == SDW_BROADCAST_DEV_NUM) {

>>> +        ctrl->fifo_status = 0;

>>> +        ret = wait_for_completion_timeout(&ctrl->sp_cmd_comp,

>>> +                          msecs_to_jiffies(TIMEOUT_MS));

>>

>> This is odd. The SoundWire spec does not handle writes to a single 

>> device or broadcast writes differently. I don't see a clear reason why 

>> you would only timeout for a broadcast write.

>>

> 

> There is danger of blocking here without timeout.


Right, and it's fine to add a timeout. The question is why add a timeout 
*only* for a broadcast operation? It should be added for every 
transaction IMO, unless you have a reason not to do so.

>>

>>> +

>>> +    /* Mask soundwire interrupts */

>>> +    ctrl->reg_write(ctrl, SWRM_INTERRUPT_MASK_ADDR,

>>> +                    SWRM_INTERRUPT_STATUS_RMSK);

>>> +

>>> +    /* Configure No pings */

>>> +    val = ctrl->reg_read(ctrl, SWRM_MCP_CFG_ADDR);

>>

>> If there is any sort of PREQ signaling for Slave-initiated interrupts, 

>> disabling PINGs is likely a non-conformant implementation since the 

>> master is required to issue a PING command within 32 frames. That's 

>> also the only way to know if a device is attached, so additional 

>> comments are likely required.

> This is the value of Maximum number of consiecutive read/write commands 

> without ping command in between. I will try to collect more details and 

> add some comments here.


Right, so it's probably the comment that's too strict. It's not no pings 
it's no pings during a sequence of up to N read/writes.
Pierre-Louis Bossart June 11, 2019, 12:21 p.m. UTC | #4
On 6/11/19 5:29 AM, Srinivas Kandagatla wrote:
> 

> 

> On 10/06/2019 15:12, Pierre-Louis Bossart wrote:

>>>>> +

>>>>> +    if (dev_addr == SDW_BROADCAST_DEV_NUM) {

>>>>> +        ctrl->fifo_status = 0;

>>>>> +        ret = wait_for_completion_timeout(&ctrl->sp_cmd_comp,

>>>>> +                          msecs_to_jiffies(TIMEOUT_MS));

>>>>

>>>> This is odd. The SoundWire spec does not handle writes to a single 

>>>> device or broadcast writes differently. I don't see a clear reason 

>>>> why you would only timeout for a broadcast write.

>>>>

>>>

>>> There is danger of blocking here without timeout.

>>

>> Right, and it's fine to add a timeout. The question is why add a 

>> timeout *only* for a broadcast operation? It should be added for every 

>> transaction IMO, unless you have a reason not to do so.

>>

> 

> I did try this before, the issue is when we read/write registers from 

> interrupt handler, these can be deadlocked as we will be interrupt 

> handler waiting for another completion interrupt, which will never 

> happen unless we return from the first interrupt.


I don't quite get the issue. With the Intel hardware we only deal with 
Master registers (some of which mirror the bus state) in the handler and 
will only modify Slave registers in the thread. All changes to Slave 
registers will be subject to a timeout as well as a check for no 
response or NAK. Not sure what is specific about your solution that 
requires a different handling of commands depending on which device 
number is used. It could very well be that you've uncovered a flaw in 
the bus design but I still don't see how it would be Qualcomm-specific?