1462 lines · 44.5 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)/* * SoundWire AMD Manager driver * * Copyright 2023-24 Advanced Micro Devices, Inc. */ #include <linux/completion.h>#include <linux/cleanup.h>#include <linux/device.h>#include <linux/io.h>#include <linux/jiffies.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_registers.h>#include <linux/pm_runtime.h>#include <linux/wait.h>#include <sound/pcm_params.h>#include <sound/soc.h>#include "bus.h"#include "amd_init.h"#include "amd_manager.h" #define DRV_NAME "amd_sdw_manager" #define to_amd_sdw(b)	container_of(b, struct amd_sdw_manager, bus) static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager){	struct sdw_bus *bus = &amd_manager->bus;	struct sdw_master_prop *prop = &bus->prop;	u32 divider; 	dev_dbg(amd_manager->dev, "mclk %d max %d row %d col %d frame_rate:%d\n",		prop->mclk_freq, prop->max_clk_freq, prop->default_row,		prop->default_col, prop->default_frame_rate); 	if (!prop->default_frame_rate || !prop->default_row) {		dev_err(amd_manager->dev, "Default frame_rate %d or row %d is invalid\n",			prop->default_frame_rate, prop->default_row);		return -EINVAL;	} 	/* Set clock divider */	divider = (prop->mclk_freq / bus->params.curr_dr_freq);	writel(divider, amd_manager->mmio + ACP_SW_CLK_FREQUENCY_CTRL); 	/* Set frame shape base on the actual bus frequency. */	prop->default_col = bus->params.curr_dr_freq /			    prop->default_frame_rate / prop->default_row;	amd_manager->cols_index = sdw_find_col_index(prop->default_col);	amd_manager->rows_index = sdw_find_row_index(prop->default_row);	bus->params.col = prop->default_col;	bus->params.row = prop->default_row;	return 0;} static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager){	u32 val;	int ret; 	writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN);	ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, val, ACP_DELAY_US,				 AMD_SDW_TIMEOUT);	if (ret)		return ret; 	/* SoundWire manager bus reset */	writel(AMD_SDW_BUS_RESET_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL);	ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL, val,				 (val & AMD_SDW_BUS_RESET_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT);	if (ret)		return ret; 	writel(AMD_SDW_BUS_RESET_CLEAR_REQ, amd_manager->mmio + ACP_SW_BUS_RESET_CTRL);	ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_BUS_RESET_CTRL, val, !val,				 ACP_DELAY_US, AMD_SDW_TIMEOUT);	if (ret) {		dev_err(amd_manager->dev, "Failed to reset SoundWire manager instance%d\n",			amd_manager->instance);		return ret;	} 	writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN);	return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, !val, ACP_DELAY_US,				  AMD_SDW_TIMEOUT);} static int amd_enable_sdw_manager(struct amd_sdw_manager *amd_manager){	u32 val; 	writel(AMD_SDW_ENABLE, amd_manager->mmio + ACP_SW_EN);	return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, val, ACP_DELAY_US,				  AMD_SDW_TIMEOUT);} static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager){	u32 val; 	writel(AMD_SDW_DISABLE, amd_manager->mmio + ACP_SW_EN);	/*	 * After invoking manager disable sequence, check whether	 * manager has executed clock stop sequence. In this case,	 * manager should ignore checking enable status register.	 */	val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);	if (val)		return 0;	return readl_poll_timeout(amd_manager->mmio + ACP_SW_EN_STATUS, val, !val, ACP_DELAY_US,				  AMD_SDW_TIMEOUT);} static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager){	u32 val; 	mutex_lock(amd_manager->acp_sdw_lock);	val = sdw_manager_reg_mask_array[amd_manager->instance];	amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance), val, val);	mutex_unlock(amd_manager->acp_sdw_lock); 	writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio +		       ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7);	writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio +		       ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);	writel(AMD_SDW_IRQ_ERROR_MASK, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);} static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager){	u32 irq_mask; 	mutex_lock(amd_manager->acp_sdw_lock);	irq_mask = sdw_manager_reg_mask_array[amd_manager->instance];	amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance),		    irq_mask, 0);	mutex_unlock(amd_manager->acp_sdw_lock); 	writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7);	writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);	writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);} static int amd_deinit_sdw_manager(struct amd_sdw_manager *amd_manager){	amd_disable_sdw_interrupts(amd_manager);	return amd_disable_sdw_manager(amd_manager);} static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager){	u32 frame_size; 	frame_size = (amd_manager->rows_index << 3) | amd_manager->cols_index;	writel(frame_size, amd_manager->mmio + ACP_SW_FRAMESIZE);} static void amd_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable){	u32 wake_ctrl; 	wake_ctrl = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);	if (enable)		wake_ctrl |= AMD_SDW_WAKE_INTR_MASK;	else		wake_ctrl &= ~AMD_SDW_WAKE_INTR_MASK; 	writel(wake_ctrl, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);} static int amd_sdw_set_device_state(struct amd_sdw_manager *amd_manager, u32 target_device_state){	u32 sdw_dev_state; 	sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE);	switch (amd_manager->instance) {	case ACP_SDW0:		u32p_replace_bits(&sdw_dev_state, target_device_state,				  AMD_SDW0_DEVICE_STATE_MASK);		break;	case ACP_SDW1:		u32p_replace_bits(&sdw_dev_state, target_device_state,				  AMD_SDW1_DEVICE_STATE_MASK);		break;	default:		return -EINVAL;	}	writel(sdw_dev_state, amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE);	sdw_dev_state = readl(amd_manager->acp_mmio + AMD_SDW_DEVICE_STATE);	dev_dbg(amd_manager->dev, "AMD_SDW_DEVICE_STATE:0x%x\n", sdw_dev_state);	return 0;} static int amd_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable){	u32 intr_cntl1;	u32 sdw_host_wake_irq_mask; 	if (!amd_manager->wake_en_mask)		return 0; 	switch (amd_manager->instance) {	case ACP_SDW0:		sdw_host_wake_irq_mask = AMD_SDW0_HOST_WAKE_INTR_MASK;		break;	case ACP_SDW1:		sdw_host_wake_irq_mask = AMD_SDW1_HOST_WAKE_INTR_MASK;		break;	default:		return -EINVAL;	} 	intr_cntl1 = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1));	if (enable)		intr_cntl1 |= sdw_host_wake_irq_mask;	else		intr_cntl1 &= ~sdw_host_wake_irq_mask;	writel(intr_cntl1, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(ACP_SDW1));	return 0;} static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_msg *msg,				  int cmd_offset){	u32 upper_data;	u32 lower_data = 0;	u16 addr;	u8 upper_addr, lower_addr;	u8 data = 0; 	addr = msg->addr + cmd_offset;	upper_addr = (addr & 0xFF00) >> 8;	lower_addr = addr & 0xFF; 	if (msg->flags == SDW_MSG_FLAG_WRITE)		data = msg->buf[cmd_offset]; 	upper_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num);	upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, msg->flags + 2);	upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, upper_addr);	lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, lower_addr);	lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data); 	*upper_word = upper_data;	*lower_word = lower_data;} static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data,				     u32 upper_data){	u64 resp;	u32 lower_resp, upper_resp;	u32 sts;	int ret; 	ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts,				 !(sts & AMD_SDW_IMM_CMD_BUSY), ACP_DELAY_US, AMD_SDW_TIMEOUT);	if (ret) {		dev_err(amd_manager->dev, "SDW%x previous cmd status clear failed\n",			amd_manager->instance);		return ret;	} 	if (sts & AMD_SDW_IMM_RES_VALID) {		dev_err(amd_manager->dev, "SDW%x manager is in bad state\n", amd_manager->instance);		writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS);	}	writel(upper_data, amd_manager->mmio + ACP_SW_IMM_CMD_UPPER_WORD);	writel(lower_data, amd_manager->mmio + ACP_SW_IMM_CMD_LOWER_QWORD); 	ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts,				 (sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT);	if (ret) {		dev_err(amd_manager->dev, "SDW%x cmd response timeout occurred\n",			amd_manager->instance);		return ret;	}	upper_resp = readl(amd_manager->mmio + ACP_SW_IMM_RESP_UPPER_WORD);	lower_resp = readl(amd_manager->mmio + ACP_SW_IMM_RESP_LOWER_QWORD); 	writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS);	ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts,				 !(sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT);	if (ret) {		dev_err(amd_manager->dev, "SDW%x cmd status retry failed\n",			amd_manager->instance);		return ret;	}	resp = upper_resp;	resp = (resp << 32) | lower_resp;	return resp;} static enum sdw_command_responseamd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg){	struct sdw_msg scp_msg = {0};	u64 response_buf[2] = {0};	u32 upper_data = 0, lower_data = 0;	int index; 	scp_msg.dev_num = msg->dev_num;	scp_msg.addr = SDW_SCP_ADDRPAGE1;	scp_msg.buf = &msg->addr_page1;	scp_msg.flags = SDW_MSG_FLAG_WRITE;	amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);	response_buf[0] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data);	scp_msg.addr = SDW_SCP_ADDRPAGE2;	scp_msg.buf = &msg->addr_page2;	amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);	response_buf[1] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data); 	for (index = 0; index < 2; index++) {		if (response_buf[index] == -ETIMEDOUT) {			dev_err_ratelimited(amd_manager->dev,					    "SCP_addrpage command timeout for Slave %d\n",					    msg->dev_num);			return SDW_CMD_TIMEOUT;		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {				dev_err_ratelimited(amd_manager->dev,						    "SCP_addrpage NACKed for Slave %d\n",						    msg->dev_num);				return SDW_CMD_FAIL;			}			dev_dbg_ratelimited(amd_manager->dev, "SCP_addrpage ignored for Slave %d\n",					    msg->dev_num);			return SDW_CMD_IGNORED;		}	}	return SDW_CMD_OK;} static int amd_prep_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg){	int ret; 	if (msg->page) {		ret = amd_program_scp_addr(amd_manager, msg);		if (ret) {			msg->len = 0;			return ret;		}	}	switch (msg->flags) {	case SDW_MSG_FLAG_READ:	case SDW_MSG_FLAG_WRITE:		break;	default:		dev_err(amd_manager->dev, "Invalid msg cmd: %d\n", msg->flags);		return -EINVAL;	}	return 0;} static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *amd_manager,						       struct sdw_msg *msg, u64 response,						       int offset){	if (response & AMD_SDW_MCP_RESP_ACK) {		if (msg->flags == SDW_MSG_FLAG_READ)			msg->buf[offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);	} else {		if (response == -ETIMEDOUT) {			dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",					    msg->dev_num);			return SDW_CMD_TIMEOUT;		} else if (response & AMD_SDW_MCP_RESP_NACK) {			dev_err_ratelimited(amd_manager->dev,					    "command response NACK received for Slave %d\n",					    msg->dev_num);			return SDW_CMD_FAIL;		}		dev_dbg_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n",				    msg->dev_num);		return SDW_CMD_IGNORED;	}	return SDW_CMD_OK;} static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg,				      int cmd_offset){	u64 response;	u32 upper_data = 0, lower_data = 0; 	amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset);	response = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data);	return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset);} static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg){	struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);	int ret, i; 	ret = amd_prep_msg(amd_manager, msg);	if (ret)		return SDW_CMD_FAIL_OTHER;	for (i = 0; i < msg->len; i++) {		ret = _amd_sdw_xfer_msg(amd_manager, msg, i);		if (ret)			return ret;	}	return SDW_CMD_OK;} static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status){	switch (status) {	case SDW_SLAVE_ATTACHED:	case SDW_SLAVE_UNATTACHED:	case SDW_SLAVE_ALERT:		amd_manager->status[index] = status;		break;	default:		amd_manager->status[index] = SDW_SLAVE_RESERVED;		break;	}} static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager){	u64 slave_stat;	u32 val;	u16 dev_index; 	/* slave status response */	slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;	dev_dbg(amd_manager->dev, "slave_stat:0x%llx\n", slave_stat);	for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {		val = (slave_stat >> (dev_index * 2)) & AMD_SDW_MCP_SLAVE_STATUS_MASK;		dev_dbg(amd_manager->dev, "val:0x%x\n", val);		amd_sdw_fill_slave_status(amd_manager, dev_index, val);	}} static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager){	u64 response; 	mutex_lock(&amd_manager->bus.msg_lock);	response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0);	mutex_unlock(&amd_manager->bus.msg_lock);	amd_sdw_process_ping_status(response, amd_manager);} static u32 amd_sdw_read_ping_status(struct sdw_bus *bus){	struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);	u64 response;	u32 slave_stat; 	response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0);	/* slave status from ping response */	slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;	dev_dbg(amd_manager->dev, "slave_stat:0x%x\n", slave_stat);	return slave_stat;} static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream){	struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);	struct sdw_transport_data t_data = {0};	struct sdw_master_runtime *m_rt;	struct sdw_port_runtime *p_rt;	struct sdw_bus_params *b_params = &bus->params;	int port_bo, hstart, hstop, sample_int;	unsigned int rate, bps, channels;	unsigned int stream_slot_size, max_slots;	static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1};	unsigned int inst_id = amd_manager->instance; 	port_bo = 0;	hstart = 1;	hstop = bus->params.col - 1;	t_data.hstop = hstop;	t_data.hstart = hstart; 	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {		rate = m_rt->stream->params.rate;		bps = m_rt->stream->params.bps;		channels = m_rt->stream->params.ch_count;		sample_int = (bus->params.curr_dr_freq / rate); 		/* Compute slots required for this stream dynamically */		stream_slot_size = bps * channels; 		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {			if (p_rt->num >= amd_manager->max_ports) {				dev_err(bus->dev, "Port %d exceeds max ports %d\n",					p_rt->num, amd_manager->max_ports);				return -EINVAL;

Lines 1–500 of 1462.