1992 lines · 49.8 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/debugfs.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/platform_device.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" #define INTEL_MASTER_SUSPEND_DELAY_MS	3000 /* * debug/config flags for the Intel SoundWire Master. * * Since we may have multiple masters active, we can have up to 8 * flags reused in each byte, with master0 using the ls-byte, etc. */ #define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME		BIT(0)#define SDW_INTEL_MASTER_DISABLE_CLOCK_STOP		BIT(1)#define SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE	BIT(2)#define SDW_INTEL_MASTER_DISABLE_MULTI_LINK		BIT(3) static int md_flags;module_param_named(sdw_md_flags, md_flags, int, 0444);MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); /* Intel SHIM Registers Definition */#define SDW_SHIM_LCAP			0x0#define SDW_SHIM_LCTL			0x4#define SDW_SHIM_IPPTR			0x8#define SDW_SHIM_SYNC			0xC #define SDW_SHIM_CTLSCAP(x)		(0x010 + 0x60 * (x))#define SDW_SHIM_CTLS0CM(x)		(0x012 + 0x60 * (x))#define SDW_SHIM_CTLS1CM(x)		(0x014 + 0x60 * (x))#define SDW_SHIM_CTLS2CM(x)		(0x016 + 0x60 * (x))#define SDW_SHIM_CTLS3CM(x)		(0x018 + 0x60 * (x))#define SDW_SHIM_PCMSCAP(x)		(0x020 + 0x60 * (x)) #define SDW_SHIM_PCMSYCHM(x, y)		(0x022 + (0x60 * (x)) + (0x2 * (y)))#define SDW_SHIM_PCMSYCHC(x, y)		(0x042 + (0x60 * (x)) + (0x2 * (y)))#define SDW_SHIM_PDMSCAP(x)		(0x062 + 0x60 * (x))#define SDW_SHIM_IOCTL(x)		(0x06C + 0x60 * (x))#define SDW_SHIM_CTMCTL(x)		(0x06E + 0x60 * (x)) #define SDW_SHIM_WAKEEN			0x190#define SDW_SHIM_WAKESTS		0x192 #define SDW_SHIM_LCTL_SPA		BIT(0)#define SDW_SHIM_LCTL_SPA_MASK		GENMASK(3, 0)#define SDW_SHIM_LCTL_CPA		BIT(8)#define SDW_SHIM_LCTL_CPA_MASK		GENMASK(11, 8) #define SDW_SHIM_SYNC_SYNCPRD_VAL_24	(24000 / SDW_CADENCE_GSYNC_KHZ - 1)#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4	(38400 / SDW_CADENCE_GSYNC_KHZ - 1)#define SDW_SHIM_SYNC_SYNCPRD		GENMASK(14, 0)#define SDW_SHIM_SYNC_SYNCCPU		BIT(15)#define SDW_SHIM_SYNC_CMDSYNC_MASK	GENMASK(19, 16)#define SDW_SHIM_SYNC_CMDSYNC		BIT(16)#define SDW_SHIM_SYNC_SYNCGO		BIT(24) #define SDW_SHIM_PCMSCAP_ISS		GENMASK(3, 0)#define SDW_SHIM_PCMSCAP_OSS		GENMASK(7, 4)#define SDW_SHIM_PCMSCAP_BSS		GENMASK(12, 8) #define SDW_SHIM_PCMSYCM_LCHN		GENMASK(3, 0)#define SDW_SHIM_PCMSYCM_HCHN		GENMASK(7, 4)#define SDW_SHIM_PCMSYCM_STREAM		GENMASK(13, 8)#define SDW_SHIM_PCMSYCM_DIR		BIT(15) #define SDW_SHIM_PDMSCAP_ISS		GENMASK(3, 0)#define SDW_SHIM_PDMSCAP_OSS		GENMASK(7, 4)#define SDW_SHIM_PDMSCAP_BSS		GENMASK(12, 8)#define SDW_SHIM_PDMSCAP_CPSS		GENMASK(15, 13) #define SDW_SHIM_IOCTL_MIF		BIT(0)#define SDW_SHIM_IOCTL_CO		BIT(1)#define SDW_SHIM_IOCTL_COE		BIT(2)#define SDW_SHIM_IOCTL_DO		BIT(3)#define SDW_SHIM_IOCTL_DOE		BIT(4)#define SDW_SHIM_IOCTL_BKE		BIT(5)#define SDW_SHIM_IOCTL_WPDD		BIT(6)#define SDW_SHIM_IOCTL_CIBD		BIT(8)#define SDW_SHIM_IOCTL_DIBD		BIT(9) #define SDW_SHIM_CTMCTL_DACTQE		BIT(0)#define SDW_SHIM_CTMCTL_DODS		BIT(1)#define SDW_SHIM_CTMCTL_DOAIS		GENMASK(4, 3) #define SDW_SHIM_WAKEEN_ENABLE		BIT(0)#define SDW_SHIM_WAKESTS_STATUS		BIT(0) /* Intel ALH Register definitions */#define SDW_ALH_STRMZCFG(x)		(0x000 + (0x4 * (x)))#define SDW_ALH_NUM_STREAMS		64 #define SDW_ALH_STRMZCFG_DMAT_VAL	0x3#define SDW_ALH_STRMZCFG_DMAT		GENMASK(7, 0)#define SDW_ALH_STRMZCFG_CHN		GENMASK(19, 16) enum intel_pdi_type {	INTEL_PDI_IN = 0,	INTEL_PDI_OUT = 1,	INTEL_PDI_BD = 2,}; #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) /* * Read, write helpers for HW registers */static inline int intel_readl(void __iomem *base, int offset){	return readl(base + offset);} static inline void intel_writel(void __iomem *base, int offset, int value){	writel(value, base + offset);} static inline u16 intel_readw(void __iomem *base, int offset){	return readw(base + offset);} static inline void intel_writew(void __iomem *base, int offset, u16 value){	writew(value, base + offset);} 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;	char *buf;	ssize_t ret;	int i, j;	unsigned int links, reg; 	buf = kzalloc(RD_BUF, GFP_KERNEL);	if (!buf)		return -ENOMEM; 	links = intel_readl(s, SDW_SHIM_LCAP) & GENMASK(2, 0); 	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 PDMSCAP, IOCTL, CTMCTL\n"); 		ret += intel_sprintf(s, false, buf, ret, SDW_SHIM_PDMSCAP(i));		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);	kfree(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 */ 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 syncprd;	u32 sync_reg; 	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+).	 */	if (prop->mclk_freq % 6000000)		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4;	else		syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; 	if (!*shim_mask) {		dev_dbg(sdw->cdns.dev, "%s: powering up all links\n", __func__); 		/* we first need to program the SyncPRD/CPU registers */		dev_dbg(sdw->cdns.dev,			"%s: first link up, programming SYNCPRD\n", __func__); 		/* 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;		}	} 	*shim_mask |= BIT(link_id); 	sdw->cdns.link_up = true;out:	mutex_unlock(sdw->link_res->shim_lock); 	return ret;} /* 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 */} static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop){	void __iomem *shim = sdw->link_res->shim;	unsigned int link_id = sdw->instance;	int ret = 0;	u16 ioctl = 0, act = 0; 	mutex_lock(sdw->link_res->shim_lock); 	/* 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); 	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); 	mutex_unlock(sdw->link_res->shim_lock); 

Lines 1–500 of 1992.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t