1096 lines · 28.2 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)// Copyright(c) 2015-17 Intel Corporation. /* * Soundwire Intel Master Driver */ #include <linux/acpi.h>#include <linux/cleanup.h>#include <linux/debugfs.h>#include <linux/delay.h>#include <linux/io.h>#include <sound/pcm_params.h>#include <linux/pm_runtime.h>#include <sound/soc.h>#include <linux/soundwire/sdw_registers.h>#include <linux/soundwire/sdw.h>#include <linux/soundwire/sdw_intel.h>#include "cadence_master.h"#include "bus.h"#include "intel.h" static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target){	int timeout = 10;	u32 reg_read; 	do {		reg_read = readl(base + offset);		if ((reg_read & mask) == target)			return 0; 		timeout--;		usleep_range(50, 100);	} while (timeout != 0); 	return -EAGAIN;} static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask){	writel(value, base + offset);	return intel_wait_bit(base, offset, mask, 0);} static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask){	writel(value, base + offset);	return intel_wait_bit(base, offset, mask, mask);} /* * debugfs */#ifdef CONFIG_DEBUG_FS #define RD_BUF (2 * PAGE_SIZE) static ssize_t intel_sprintf(void __iomem *mem, bool l,			     char *buf, size_t pos, unsigned int reg){	int value; 	if (l)		value = intel_readl(mem, reg);	else		value = intel_readw(mem, reg); 	return scnprintf(buf + pos, RD_BUF - pos, "%4x\t%4x\n", reg, value);} static int intel_reg_show(struct seq_file *s_file, void *data){	struct sdw_intel *sdw = s_file->private;	void __iomem *s = sdw->link_res->shim;	void __iomem *a = sdw->link_res->alh;	ssize_t ret;	int i, j;	unsigned int links, reg; 	char *buf __free(kfree) = kzalloc(RD_BUF, GFP_KERNEL);	if (!buf)		return -ENOMEM; 	links = intel_readl(s, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_LCOUNT_MASK; 	ret = scnprintf(buf, RD_BUF, "Register  Value\n");	ret += scnprintf(buf + ret, RD_BUF - ret, "\nShim\n"); 	for (i = 0; i < links; i++) {		reg = SDW_SHIM_LCAP + i * 4;		ret += intel_sprintf(s, true, buf, ret, reg);	} 	for (i = 0; i < links; i++) {		ret += scnprintf(buf + ret, RD_BUF - ret, "\nLink%d\n", i);		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLSCAP(i));		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS0CM(i));		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS1CM(i));		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS2CM(i));		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTLS3CM(i));		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PCMSCAP(i)); 		ret += scnprintf(buf + ret, RD_BUF - ret, "\n PCMSyCH registers\n"); 		/*		 * the value 10 is the number of PDIs. We will need a		 * cleanup to remove hard-coded Intel configurations		 * from cadence_master.c		 */		for (j = 0; j < 10; j++) {			ret += intel_sprintf(s, false, buf, ret,					SDW_SHIM_PCMSYCHM(i, j));			ret += intel_sprintf(s, false, buf, ret,					SDW_SHIM_PCMSYCHC(i, j));		}		ret += scnprintf(buf + ret, RD_BUF - ret, "\n IOCTL, CTMCTL\n"); 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_IOCTL(i));		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_CTMCTL(i));	} 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nWake registers\n");	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKEEN);	ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_WAKESTS); 	ret += scnprintf(buf + ret, RD_BUF - ret, "\nALH STRMzCFG\n");	for (i = 0; i < SDW_ALH_NUM_STREAMS; i++)		ret += intel_sprintf(a, true, buf, ret, SDW_ALH_STRMZCFG(i)); 	seq_printf(s_file, "%s", buf); 	return 0;}DEFINE_SHOW_ATTRIBUTE(intel_reg); static int intel_set_m_datamode(void *data, u64 value){	struct sdw_intel *sdw = data;	struct sdw_bus *bus = &sdw->cdns.bus; 	if (value > SDW_PORT_DATA_MODE_STATIC_1)		return -EINVAL; 	/* Userspace changed the hardware state behind the kernel's back */	add_taint(TAINT_USER, LOCKDEP_STILL_OK); 	bus->params.m_data_mode = value; 	return 0;}DEFINE_DEBUGFS_ATTRIBUTE(intel_set_m_datamode_fops, NULL,			 intel_set_m_datamode, "%llu\n"); static int intel_set_s_datamode(void *data, u64 value){	struct sdw_intel *sdw = data;	struct sdw_bus *bus = &sdw->cdns.bus; 	if (value > SDW_PORT_DATA_MODE_STATIC_1)		return -EINVAL; 	/* Userspace changed the hardware state behind the kernel's back */	add_taint(TAINT_USER, LOCKDEP_STILL_OK); 	bus->params.s_data_mode = value; 	return 0;}DEFINE_DEBUGFS_ATTRIBUTE(intel_set_s_datamode_fops, NULL,			 intel_set_s_datamode, "%llu\n"); static void intel_debugfs_init(struct sdw_intel *sdw){	struct dentry *root = sdw->cdns.bus.debugfs; 	if (!root)		return; 	sdw->debugfs = debugfs_create_dir("intel-sdw", root); 	debugfs_create_file("intel-registers", 0400, sdw->debugfs, sdw,			    &intel_reg_fops); 	debugfs_create_file("intel-m-datamode", 0200, sdw->debugfs, sdw,			    &intel_set_m_datamode_fops); 	debugfs_create_file("intel-s-datamode", 0200, sdw->debugfs, sdw,			    &intel_set_s_datamode_fops); 	sdw_cdns_debugfs_init(&sdw->cdns, sdw->debugfs);} static void intel_debugfs_exit(struct sdw_intel *sdw){	debugfs_remove_recursive(sdw->debugfs);}#elsestatic void intel_debugfs_init(struct sdw_intel *sdw) {}static void intel_debugfs_exit(struct sdw_intel *sdw) {}#endif /* CONFIG_DEBUG_FS */ /* * shim ops *//* this needs to be called with shim_lock */static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw){	void __iomem *shim = sdw->link_res->shim;	unsigned int link_id = sdw->instance;	u16 ioctl; 	/* Switch to MIP from Glue logic */	ioctl = intel_readw(shim,  SDW_SHIM_IOCTL(link_id)); 	ioctl &= ~(SDW_SHIM_IOCTL_DOE);	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl &= ~(SDW_SHIM_IOCTL_DO);	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl |= (SDW_SHIM_IOCTL_MIF);	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl &= ~(SDW_SHIM_IOCTL_BKE);	ioctl &= ~(SDW_SHIM_IOCTL_COE);	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	/* at this point Master IP has full control of the I/Os */} /* this needs to be called with shim_lock */static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw){	unsigned int link_id = sdw->instance;	void __iomem *shim = sdw->link_res->shim;	u16 ioctl; 	/* Glue logic */	ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id));	ioctl |= SDW_SHIM_IOCTL_BKE;	ioctl |= SDW_SHIM_IOCTL_COE;	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl &= ~(SDW_SHIM_IOCTL_MIF);	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	/* at this point Integration Glue has full control of the I/Os */} /* this needs to be called with shim_lock */static void intel_shim_init(struct sdw_intel *sdw){	void __iomem *shim = sdw->link_res->shim;	unsigned int link_id = sdw->instance;	u16 ioctl = 0, act; 	/* Initialize Shim */	ioctl |= SDW_SHIM_IOCTL_BKE;	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl |= SDW_SHIM_IOCTL_WPDD;	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl |= SDW_SHIM_IOCTL_DO;	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	ioctl |= SDW_SHIM_IOCTL_DOE;	intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl);	usleep_range(10, 15); 	intel_shim_glue_to_master_ip(sdw); 	act = intel_readw(shim, SDW_SHIM_CTMCTL(link_id));	u16p_replace_bits(&act, 0x1, SDW_SHIM_CTMCTL_DOAIS);	act |= SDW_SHIM_CTMCTL_DACTQE;	act |= SDW_SHIM_CTMCTL_DODS;	intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act);	usleep_range(10, 15);} static int intel_shim_check_wake(struct sdw_intel *sdw){	void __iomem *shim;	u16 wake_sts; 	shim = sdw->link_res->shim;	wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); 	return wake_sts & BIT(sdw->instance);} static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable){	void __iomem *shim = sdw->link_res->shim;	unsigned int link_id = sdw->instance;	u16 wake_en, wake_sts; 	mutex_lock(sdw->link_res->shim_lock);	wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); 	if (wake_enable) {		/* Enable the wakeup */		wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id);		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en);	} else {		/* Disable the wake up interrupt */		wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id);		intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); 		/* Clear wake status */		wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS);		wake_sts |= (SDW_SHIM_WAKESTS_STATUS << link_id);		intel_writew(shim, SDW_SHIM_WAKESTS, wake_sts);	}	mutex_unlock(sdw->link_res->shim_lock);} static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw){	void __iomem *shim = sdw->link_res->shim;	int sync_reg; 	sync_reg = intel_readl(shim, SDW_SHIM_SYNC);	return !!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK);} static int intel_link_power_up(struct sdw_intel *sdw){	unsigned int link_id = sdw->instance;	void __iomem *shim = sdw->link_res->shim;	u32 *shim_mask = sdw->link_res->shim_mask;	struct sdw_bus *bus = &sdw->cdns.bus;	struct sdw_master_prop *prop = &bus->prop;	u32 spa_mask, cpa_mask;	u32 link_control;	int ret = 0;	u32 clock_source;	u32 syncprd;	u32 sync_reg;	bool lcap_mlcs; 	mutex_lock(sdw->link_res->shim_lock); 	/*	 * The hardware relies on an internal counter, typically 4kHz,	 * to generate the SoundWire SSP - which defines a 'safe'	 * synchronization point between commands and audio transport	 * and allows for multi link synchronization. The SYNCPRD value	 * is only dependent on the oscillator clock provided to	 * the IP, so adjust based on _DSD properties reported in DSDT	 * tables. The values reported are based on either 24MHz	 * (CNL/CML) or 38.4 MHz (ICL/TGL+). On MeteorLake additional	 * frequencies are available with the MLCS clock source selection.	 */	lcap_mlcs = intel_readl(shim, SDW_SHIM_LCAP) & SDW_SHIM_LCAP_MLCS_MASK; 	if (prop->mclk_freq % 6000000) {		if (prop->mclk_freq % 2400000) {			if (lcap_mlcs) {				syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24_576;				clock_source = SDW_SHIM_MLCS_CARDINAL_CLK;			} else {				dev_err(sdw->cdns.dev, "%s: invalid clock configuration, mclk %d lcap_mlcs %d\n",					__func__, prop->mclk_freq, lcap_mlcs);				ret = -EINVAL;				goto out;			}		} else {			syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;			clock_source = SDW_SHIM_MLCS_XTAL_CLK;		}	} else {		if (lcap_mlcs) {			syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_96;			clock_source = SDW_SHIM_MLCS_AUDIO_PLL_CLK;		} else {			syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24;			clock_source = SDW_SHIM_MLCS_XTAL_CLK;		}	} 	if (!*shim_mask) {		dev_dbg(sdw->cdns.dev, "powering up all links\n"); 		/* we first need to program the SyncPRD/CPU registers */		dev_dbg(sdw->cdns.dev,			"first link up, programming SYNCPRD\n"); 		/* set SyncPRD period */		sync_reg = intel_readl(shim, SDW_SHIM_SYNC);		u32p_replace_bits(&sync_reg, syncprd, SDW_SHIM_SYNC_SYNCPRD); 		/* Set SyncCPU bit */		sync_reg |= SDW_SHIM_SYNC_SYNCCPU;		intel_writel(shim, SDW_SHIM_SYNC, sync_reg); 		/* Link power up sequence */		link_control = intel_readl(shim, SDW_SHIM_LCTL); 		/* only power-up enabled links */		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, sdw->link_res->link_mask);		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 		link_control |=  spa_mask; 		ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);		if (ret < 0) {			dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret);			goto out;		} 		/* SyncCPU will change once link is active */		ret = intel_wait_bit(shim, SDW_SHIM_SYNC,				     SDW_SHIM_SYNC_SYNCCPU, 0);		if (ret < 0) {			dev_err(sdw->cdns.dev,				"Failed to set SHIM_SYNC: %d\n", ret);			goto out;		} 		/* update link clock if needed */		if (lcap_mlcs) {			link_control = intel_readl(shim, SDW_SHIM_LCTL);			u32p_replace_bits(&link_control, clock_source, SDW_SHIM_LCTL_MLCS_MASK);			intel_writel(shim, SDW_SHIM_LCTL, link_control);		}	} 	*shim_mask |= BIT(link_id); 	sdw->cdns.link_up = true; 	intel_shim_init(sdw); out:	mutex_unlock(sdw->link_res->shim_lock); 	return ret;} static int intel_link_power_down(struct sdw_intel *sdw){	u32 link_control, spa_mask, cpa_mask;	unsigned int link_id = sdw->instance;	void __iomem *shim = sdw->link_res->shim;	u32 *shim_mask = sdw->link_res->shim_mask;	int ret = 0; 	mutex_lock(sdw->link_res->shim_lock); 	if (!(*shim_mask & BIT(link_id)))		dev_err(sdw->cdns.dev,			"%s: Unbalanced power-up/down calls\n", __func__); 	sdw->cdns.link_up = false; 	intel_shim_master_ip_to_glue(sdw); 	*shim_mask &= ~BIT(link_id); 	if (!*shim_mask) { 		dev_dbg(sdw->cdns.dev, "powering down all links\n"); 		/* Link power down sequence */		link_control = intel_readl(shim, SDW_SHIM_LCTL); 		/* only power-down enabled links */		spa_mask = FIELD_PREP(SDW_SHIM_LCTL_SPA_MASK, ~sdw->link_res->link_mask);		cpa_mask = FIELD_PREP(SDW_SHIM_LCTL_CPA_MASK, sdw->link_res->link_mask); 		link_control &=  spa_mask; 		ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask);		if (ret < 0) {			dev_err(sdw->cdns.dev, "%s: could not power down link\n", __func__); 			/*			 * we leave the sdw->cdns.link_up flag as false since we've disabled			 * the link at this point and cannot handle interrupts any longer.			 */		}	} 	mutex_unlock(sdw->link_res->shim_lock); 	return ret;} static void intel_shim_sync_arm(struct sdw_intel *sdw)

Lines 1–500 of 1096.