Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
This commit is contained in:
		
						commit
						4a2447b483
					
				| @ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; | ||||
| #define AZX_REG_SD_BDLPL		0x18 | ||||
| #define AZX_REG_SD_BDLPU		0x1c | ||||
| 
 | ||||
| /* GTS registers */ | ||||
| #define AZX_REG_LLCH			0x14 | ||||
| 
 | ||||
| #define AZX_REG_GTS_BASE		0x520 | ||||
| 
 | ||||
| #define AZX_REG_GTSCC	(AZX_REG_GTS_BASE + 0x00) | ||||
| #define AZX_REG_WALFCC	(AZX_REG_GTS_BASE + 0x04) | ||||
| #define AZX_REG_TSCCL	(AZX_REG_GTS_BASE + 0x08) | ||||
| #define AZX_REG_TSCCU	(AZX_REG_GTS_BASE + 0x0C) | ||||
| #define AZX_REG_LLPFOC	(AZX_REG_GTS_BASE + 0x14) | ||||
| #define AZX_REG_LLPCL	(AZX_REG_GTS_BASE + 0x18) | ||||
| #define AZX_REG_LLPCU	(AZX_REG_GTS_BASE + 0x1C) | ||||
| 
 | ||||
| /* Haswell/Broadwell display HD-A controller Extended Mode registers */ | ||||
| #define AZX_REG_HSW_EM4			0x100c | ||||
| #define AZX_REG_HSW_EM5			0x1010 | ||||
| @ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; | ||||
| /* Interval used to calculate the iterating register offset */ | ||||
| #define AZX_DRSM_INTERVAL		0x08 | ||||
| 
 | ||||
| /* Global time synchronization registers */ | ||||
| #define GTSCC_TSCCD_MASK		0x80000000 | ||||
| #define GTSCC_TSCCD_SHIFT		BIT(31) | ||||
| #define GTSCC_TSCCI_MASK		0x20 | ||||
| #define GTSCC_CDMAS_DMA_DIR_SHIFT	4 | ||||
| 
 | ||||
| #define WALFCC_CIF_MASK			0x1FF | ||||
| #define WALFCC_FN_SHIFT			9 | ||||
| #define HDA_CLK_CYCLES_PER_FRAME	512 | ||||
| 
 | ||||
| /*
 | ||||
|  * An error occurs near frame "rollover". The clocks in frame value indicates | ||||
|  * whether this error may have occurred. Here we use the value of 10. Please | ||||
|  * see the errata for the right number [<10] | ||||
|  */ | ||||
| #define HDA_MAX_CYCLE_VALUE		499 | ||||
| #define HDA_MAX_CYCLE_OFFSET		10 | ||||
| #define HDA_MAX_CYCLE_READ_RETRY	10 | ||||
| 
 | ||||
| #define TSCCU_CCU_SHIFT			32 | ||||
| #define LLPC_CCU_SHIFT			32 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * helpers to read the stream position | ||||
|  */ | ||||
|  | ||||
| @ -245,6 +245,12 @@ struct hdac_rb { | ||||
| 
 | ||||
| /*
 | ||||
|  * HD-audio bus base driver | ||||
|  * | ||||
|  * @ppcap: pp capabilities pointer | ||||
|  * @spbcap: SPIB capabilities pointer | ||||
|  * @mlcap: MultiLink capabilities pointer | ||||
|  * @gtscap: gts capabilities pointer | ||||
|  * @drsmcap: dma resume capabilities pointer | ||||
|  */ | ||||
| struct hdac_bus { | ||||
| 	struct device *dev; | ||||
| @ -256,6 +262,12 @@ struct hdac_bus { | ||||
| 	void __iomem *remap_addr; | ||||
| 	int irq; | ||||
| 
 | ||||
| 	void __iomem *ppcap; | ||||
| 	void __iomem *spbcap; | ||||
| 	void __iomem *mlcap; | ||||
| 	void __iomem *gtscap; | ||||
| 	void __iomem *drsmcap; | ||||
| 
 | ||||
| 	/* codec linked list */ | ||||
| 	struct list_head codec_list; | ||||
| 	unsigned int num_codecs; | ||||
| @ -335,6 +347,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) | ||||
| int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val); | ||||
| int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, | ||||
| 			      unsigned int *res); | ||||
| int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus); | ||||
| int snd_hdac_link_power(struct hdac_device *codec, bool enable); | ||||
| 
 | ||||
| bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset); | ||||
|  | ||||
| @ -8,11 +8,6 @@ | ||||
|  * | ||||
|  * @bus: hdac bus | ||||
|  * @num_streams: streams supported | ||||
|  * @ppcap: pp capabilities pointer | ||||
|  * @spbcap: SPIB capabilities pointer | ||||
|  * @mlcap: MultiLink capabilities pointer | ||||
|  * @gtscap: gts capabilities pointer | ||||
|  * @drsmcap: dma resume capabilities pointer | ||||
|  * @hlink_list: link list of HDA links | ||||
|  * @lock: lock for link mgmt | ||||
|  * @cmd_dma_state: state of cmd DMAs: CORB and RIRB | ||||
| @ -22,12 +17,6 @@ struct hdac_ext_bus { | ||||
| 	int num_streams; | ||||
| 	int idx; | ||||
| 
 | ||||
| 	void __iomem *ppcap; | ||||
| 	void __iomem *spbcap; | ||||
| 	void __iomem *mlcap; | ||||
| 	void __iomem *gtscap; | ||||
| 	void __iomem *drsmcap; | ||||
| 
 | ||||
| 	struct list_head hlink_list; | ||||
| 
 | ||||
| 	struct mutex lock; | ||||
| @ -54,7 +43,6 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); | ||||
| #define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \ | ||||
| 	HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data) | ||||
| 
 | ||||
| int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *sbus); | ||||
| void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable); | ||||
| void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable); | ||||
| 
 | ||||
|  | ||||
| @ -13,3 +13,4 @@ header-y += sb16_csp.h | ||||
| header-y += sfnt_info.h | ||||
| header-y += tlv.h | ||||
| header-y += usb_stream.h | ||||
| header-y += snd_sst_tokens.h | ||||
|  | ||||
							
								
								
									
										214
									
								
								include/uapi/sound/snd_sst_tokens.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								include/uapi/sound/snd_sst_tokens.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | ||||
| /*
 | ||||
|  * snd_sst_tokens.h - Intel SST tokens definition | ||||
|  * | ||||
|  * Copyright (C) 2016 Intel Corp | ||||
|  * Author: Shreyas NC <shreyas.nc@intel.com> | ||||
|  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as version 2, as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  */ | ||||
| #ifndef __SND_SST_TOKENS_H__ | ||||
| #define __SND_SST_TOKENS_H__ | ||||
| 
 | ||||
| /**
 | ||||
|  * %SKL_TKN_UUID:               Module UUID | ||||
|  * | ||||
|  * %SKL_TKN_U8_BLOCK_TYPE:      Type of the private data block.Can be: | ||||
|  *                              tuples, bytes, short and words | ||||
|  * | ||||
|  * %SKL_TKN_U8_IN_PIN_TYPE:     Input pin type, | ||||
|  *                              homogenous=0, heterogenous=1 | ||||
|  * | ||||
|  * %SKL_TKN_U8_OUT_PIN_TYPE:    Output pin type, | ||||
|  *                              homogenous=0, heterogenous=1 | ||||
|  * %SKL_TKN_U8_DYN_IN_PIN:      Configure Input pin dynamically | ||||
|  *                              if true | ||||
|  * | ||||
|  * %SKL_TKN_U8_DYN_OUT_PIN:     Configure Output pin dynamically | ||||
|  *                              if true | ||||
|  * | ||||
|  * %SKL_TKN_U8_IN_QUEUE_COUNT:  Store the number of Input pins | ||||
|  * | ||||
|  * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins | ||||
|  * | ||||
|  * %SKL_TKN_U8_TIME_SLOT:       TDM slot number | ||||
|  * | ||||
|  * %SKL_TKN_U8_CORE_ID:         Stores module affinity value.Can take | ||||
|  *                              the values: | ||||
|  *                              SKL_AFFINITY_CORE_0 = 0, | ||||
|  *                              SKL_AFFINITY_CORE_1, | ||||
|  *                              SKL_AFFINITY_CORE_MAX | ||||
|  * | ||||
|  * %SKL_TKN_U8_MOD_TYPE:        Module type value. | ||||
|  * | ||||
|  * %SKL_TKN_U8_CONN_TYPE:       Module connection type can be a FE, | ||||
|  *                              BE or NONE as defined : | ||||
|  *                              SKL_PIPE_CONN_TYPE_NONE = 0, | ||||
|  *                              SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) | ||||
|  *                              SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) | ||||
|  * | ||||
|  * %SKL_TKN_U8_DEV_TYPE:        Type of device to which the module is | ||||
|  *                              connected | ||||
|  *                              Can take the values: | ||||
|  *                              SKL_DEVICE_BT = 0x0, | ||||
|  *                              SKL_DEVICE_DMIC = 0x1, | ||||
|  *                              SKL_DEVICE_I2S = 0x2, | ||||
|  *                              SKL_DEVICE_SLIMBUS = 0x3, | ||||
|  *                              SKL_DEVICE_HDALINK = 0x4, | ||||
|  *                              SKL_DEVICE_HDAHOST = 0x5, | ||||
|  *                              SKL_DEVICE_NONE | ||||
|  * | ||||
|  * %SKL_TKN_U8_HW_CONN_TYPE:    Connection type of the HW to which the | ||||
|  *                              module is connected | ||||
|  *                              SKL_CONN_NONE = 0, | ||||
|  *                              SKL_CONN_SOURCE = 1, | ||||
|  *                              SKL_CONN_SINK = 2 | ||||
|  * | ||||
|  * %SKL_TKN_U16_PIN_INST_ID:    Stores the pin instance id | ||||
|  * | ||||
|  * %SKL_TKN_U16_MOD_INST_ID:    Stores the mdule instance id | ||||
|  * | ||||
|  * %SKL_TKN_U32_MAX_MCPS:       Module max mcps value | ||||
|  * | ||||
|  * %SKL_TKN_U32_MEM_PAGES:      Module resource pages | ||||
|  * | ||||
|  * %SKL_TKN_U32_OBS:            Stores Output Buffer size | ||||
|  * | ||||
|  * %SKL_TKN_U32_IBS:            Stores input buffer size | ||||
|  * | ||||
|  * %SKL_TKN_U32_VBUS_ID:        Module VBUS_ID. PDM=0, SSP0=0, | ||||
|  *                              SSP1=1,SSP2=2, | ||||
|  *                              SSP3=3, SSP4=4, | ||||
|  *                              SSP5=5, SSP6=6,INVALID | ||||
|  * | ||||
|  * %SKL_TKN_U32_PARAMS_FIXUP:   Module Params fixup mask | ||||
|  * %SKL_TKN_U32_CONVERTER:      Module params converter mask | ||||
|  * %SKL_TKN_U32_PIPE_ID:        Stores the pipe id | ||||
|  * | ||||
|  * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is | ||||
|  *                              connected to. It can be | ||||
|  *                              SKL_PIPE_CONN_TYPE_NONE = 0, | ||||
|  *                              SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), | ||||
|  *                              SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), | ||||
|  * | ||||
|  * %SKL_TKN_U32_PIPE_PRIORITY:  Pipe priority value | ||||
|  * %SKL_TKN_U32_PIPE_MEM_PGS:   Pipe resource pages | ||||
|  * | ||||
|  * %SKL_TKN_U32_DIR_PIN_COUNT:  Value for the direction to set input/output | ||||
|  *                              formats and the pin count. | ||||
|  *                              The first 4 bits have the direction | ||||
|  *                              value and the next 4 have | ||||
|  *                              the pin count value. | ||||
|  *                              SKL_DIR_IN = 0, SKL_DIR_OUT = 1. | ||||
|  *                              The input and output formats | ||||
|  *                              share the same set of tokens | ||||
|  *                              with the distinction between input | ||||
|  *                              and output made by reading direction | ||||
|  *                              token. | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_CH:         Supported channel count | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_FREQ:       Supported frequency/sample rate | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_BIT_DEPTH:  Supported container size | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_CH_CONFIG:  Supported channel configurations for the | ||||
|  *                              input/output. | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per | ||||
|  *                              channel or per sample. The values can be : | ||||
|  *                              SKL_INTERLEAVING_PER_CHANNEL = 0, | ||||
|  *                              SKL_INTERLEAVING_PER_SAMPLE = 1, | ||||
|  * | ||||
|  * %SKL_TKN_U32_FMT_SAMPLE_TYPE: | ||||
|  *                              Specifies the sample type. Can take the | ||||
|  *                              values: SKL_SAMPLE_TYPE_INT_MSB = 0, | ||||
|  *                              SKL_SAMPLE_TYPE_INT_LSB = 1, | ||||
|  *                              SKL_SAMPLE_TYPE_INT_SIGNED = 2, | ||||
|  *                              SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, | ||||
|  *                              SKL_SAMPLE_TYPE_FLOAT = 4 | ||||
|  * | ||||
|  * %SKL_TKN_U32_CH_MAP:         Channel map values | ||||
|  * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: | ||||
|  *                              SKL_PARAM_DEFAULT, SKL_PARAM_INIT, | ||||
|  *                              SKL_PARAM_SET, SKL_PARAM_BIND | ||||
|  * | ||||
|  * %SKL_TKN_U32_MOD_PARAM_ID:   ID of the module params | ||||
|  * | ||||
|  * %SKL_TKN_U32_CAPS_SET_PARAMS: | ||||
|  *                              Set params value | ||||
|  * | ||||
|  * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID | ||||
|  * | ||||
|  * %SKL_TKN_U32_CAPS_SIZE:      Caps size | ||||
|  * | ||||
|  * %SKL_TKN_U32_PROC_DOMAIN:    Specify processing domain | ||||
|  * | ||||
|  * %SKL_TKN_U32_LIB_COUNT:      Specifies the number of libraries | ||||
|  * | ||||
|  * %SKL_TKN_STR_LIB_NAME:       Specifies the library name | ||||
|  * | ||||
|  * module_id and loadable flags dont have tokens as these values will be | ||||
|  * read from the DSP FW manifest | ||||
|  */ | ||||
| enum SKL_TKNS { | ||||
| 	SKL_TKN_UUID = 1, | ||||
| 	SKL_TKN_U8_NUM_BLOCKS, | ||||
| 	SKL_TKN_U8_BLOCK_TYPE, | ||||
| 	SKL_TKN_U8_IN_PIN_TYPE, | ||||
| 	SKL_TKN_U8_OUT_PIN_TYPE, | ||||
| 	SKL_TKN_U8_DYN_IN_PIN, | ||||
| 	SKL_TKN_U8_DYN_OUT_PIN, | ||||
| 	SKL_TKN_U8_IN_QUEUE_COUNT, | ||||
| 	SKL_TKN_U8_OUT_QUEUE_COUNT, | ||||
| 	SKL_TKN_U8_TIME_SLOT, | ||||
| 	SKL_TKN_U8_CORE_ID, | ||||
| 	SKL_TKN_U8_MOD_TYPE, | ||||
| 	SKL_TKN_U8_CONN_TYPE, | ||||
| 	SKL_TKN_U8_DEV_TYPE, | ||||
| 	SKL_TKN_U8_HW_CONN_TYPE, | ||||
| 	SKL_TKN_U16_MOD_INST_ID, | ||||
| 	SKL_TKN_U16_BLOCK_SIZE, | ||||
| 	SKL_TKN_U32_MAX_MCPS, | ||||
| 	SKL_TKN_U32_MEM_PAGES, | ||||
| 	SKL_TKN_U32_OBS, | ||||
| 	SKL_TKN_U32_IBS, | ||||
| 	SKL_TKN_U32_VBUS_ID, | ||||
| 	SKL_TKN_U32_PARAMS_FIXUP, | ||||
| 	SKL_TKN_U32_CONVERTER, | ||||
| 	SKL_TKN_U32_PIPE_ID, | ||||
| 	SKL_TKN_U32_PIPE_CONN_TYPE, | ||||
| 	SKL_TKN_U32_PIPE_PRIORITY, | ||||
| 	SKL_TKN_U32_PIPE_MEM_PGS, | ||||
| 	SKL_TKN_U32_DIR_PIN_COUNT, | ||||
| 	SKL_TKN_U32_FMT_CH, | ||||
| 	SKL_TKN_U32_FMT_FREQ, | ||||
| 	SKL_TKN_U32_FMT_BIT_DEPTH, | ||||
| 	SKL_TKN_U32_FMT_SAMPLE_SIZE, | ||||
| 	SKL_TKN_U32_FMT_CH_CONFIG, | ||||
| 	SKL_TKN_U32_FMT_INTERLEAVE, | ||||
| 	SKL_TKN_U32_FMT_SAMPLE_TYPE, | ||||
| 	SKL_TKN_U32_FMT_CH_MAP, | ||||
| 	SKL_TKN_U32_PIN_MOD_ID, | ||||
| 	SKL_TKN_U32_PIN_INST_ID, | ||||
| 	SKL_TKN_U32_MOD_SET_PARAMS, | ||||
| 	SKL_TKN_U32_MOD_PARAM_ID, | ||||
| 	SKL_TKN_U32_CAPS_SET_PARAMS, | ||||
| 	SKL_TKN_U32_CAPS_PARAMS_ID, | ||||
| 	SKL_TKN_U32_CAPS_SIZE, | ||||
| 	SKL_TKN_U32_PROC_DOMAIN, | ||||
| 	SKL_TKN_U32_LIB_COUNT, | ||||
| 	SKL_TKN_STR_LIB_NAME, | ||||
| 	SKL_TKN_MAX = SKL_TKN_STR_LIB_NAME, | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| @ -29,81 +29,6 @@ | ||||
|  */ | ||||
| #define HDAC_MAX_CAPS 10 | ||||
| 
 | ||||
| /**
 | ||||
|  * snd_hdac_ext_bus_parse_capabilities - parse capablity structure | ||||
|  * @ebus: the pointer to extended bus object | ||||
|  * | ||||
|  * Returns 0 if successful, or a negative error code. | ||||
|  */ | ||||
| int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus) | ||||
| { | ||||
| 	unsigned int cur_cap; | ||||
| 	unsigned int offset; | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 	unsigned int counter = 0; | ||||
| 
 | ||||
| 	offset = snd_hdac_chip_readl(bus, LLCH); | ||||
| 
 | ||||
| 	/* Lets walk the linked capabilities list */ | ||||
| 	do { | ||||
| 		cur_cap = _snd_hdac_chip_read(l, bus, offset); | ||||
| 
 | ||||
| 		dev_dbg(bus->dev, "Capability version: 0x%x\n", | ||||
| 				((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF)); | ||||
| 
 | ||||
| 		dev_dbg(bus->dev, "HDA capability ID: 0x%x\n", | ||||
| 				(cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF); | ||||
| 
 | ||||
| 		switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) { | ||||
| 		case AZX_ML_CAP_ID: | ||||
| 			dev_dbg(bus->dev, "Found ML capability\n"); | ||||
| 			ebus->mlcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_GTS_CAP_ID: | ||||
| 			dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset); | ||||
| 			ebus->gtscap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_PP_CAP_ID: | ||||
| 			/* PP capability found, the Audio DSP is present */ | ||||
| 			dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset); | ||||
| 			ebus->ppcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_SPB_CAP_ID: | ||||
| 			/* SPIB capability found, handler function */ | ||||
| 			dev_dbg(bus->dev, "Found SPB capability\n"); | ||||
| 			ebus->spbcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_DRSM_CAP_ID: | ||||
| 			/* DMA resume  capability found, handler function */ | ||||
| 			dev_dbg(bus->dev, "Found DRSM capability\n"); | ||||
| 			ebus->drsmcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		counter++; | ||||
| 
 | ||||
| 		if (counter > HDAC_MAX_CAPS) { | ||||
| 			dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n"); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		/* read the offset of next capabiity */ | ||||
| 		offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK; | ||||
| 
 | ||||
| 	} while (offset); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities); | ||||
| 
 | ||||
| /*
 | ||||
|  * processing pipe helpers - these helpers are useful for dealing with HDA | ||||
|  * new capability of processing pipelines | ||||
| @ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) | ||||
| { | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->ppcap) { | ||||
| 	if (!bus->ppcap) { | ||||
| 		dev_err(bus->dev, "Address of PP capability is NULL"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN); | ||||
| 		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN); | ||||
| 	else | ||||
| 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0); | ||||
| 		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); | ||||
| 
 | ||||
| @ -139,15 +64,15 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable) | ||||
| { | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->ppcap) { | ||||
| 	if (!bus->ppcap) { | ||||
| 		dev_err(bus->dev, "Address of PP capability is NULL\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE); | ||||
| 		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE); | ||||
| 	else | ||||
| 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0); | ||||
| 		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); | ||||
| 
 | ||||
| @ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) | ||||
| 	struct hdac_ext_link *hlink; | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1; | ||||
| 	link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; | ||||
| 
 | ||||
| 	dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count); | ||||
| 
 | ||||
| @ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) | ||||
| 			return -ENOMEM; | ||||
| 		hlink->index = idx; | ||||
| 		hlink->bus = bus; | ||||
| 		hlink->ml_addr = ebus->mlcap + AZX_ML_BASE + | ||||
| 		hlink->ml_addr = bus->mlcap + AZX_ML_BASE + | ||||
| 					(AZX_ML_INTERVAL * idx); | ||||
| 		hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP); | ||||
| 		hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); | ||||
|  | ||||
| @ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, | ||||
| { | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (ebus->ppcap) { | ||||
| 		stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE + | ||||
| 	if (bus->ppcap) { | ||||
| 		stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + | ||||
| 				AZX_PPHC_INTERVAL * idx; | ||||
| 
 | ||||
| 		stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE + | ||||
| 		stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + | ||||
| 				AZX_PPLC_MULTI * ebus->num_streams + | ||||
| 				AZX_PPLC_INTERVAL * idx; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ebus->spbcap) { | ||||
| 		stream->spib_addr = ebus->spbcap + AZX_SPB_BASE + | ||||
| 	if (bus->spbcap) { | ||||
| 		stream->spib_addr = bus->spbcap + AZX_SPB_BASE + | ||||
| 					AZX_SPB_INTERVAL * idx + | ||||
| 					AZX_SPB_SPIB; | ||||
| 
 | ||||
| 		stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE + | ||||
| 		stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + | ||||
| 					AZX_SPB_INTERVAL * idx + | ||||
| 					AZX_SPB_MAXFIFO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ebus->drsmcap) | ||||
| 		stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE + | ||||
| 	if (bus->drsmcap) | ||||
| 		stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + | ||||
| 					AZX_DRSM_INTERVAL * idx; | ||||
| 
 | ||||
| 	stream->decoupled = false; | ||||
| @ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, | ||||
| 
 | ||||
| 	spin_lock_irq(&bus->reg_lock); | ||||
| 	if (decouple) | ||||
| 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, | ||||
| 		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, | ||||
| 				AZX_PPCTL_PROCEN(hstream->index)); | ||||
| 	else | ||||
| 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, | ||||
| 		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, | ||||
| 					AZX_PPCTL_PROCEN(hstream->index), 0); | ||||
| 	stream->decoupled = decouple; | ||||
| 	spin_unlock_irq(&bus->reg_lock); | ||||
| @ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, | ||||
| 	struct hdac_stream *stream = NULL; | ||||
| 	struct hdac_bus *hbus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->ppcap) { | ||||
| 	if (!hbus->ppcap) { | ||||
| 		dev_err(hbus->dev, "stream type not supported\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| @ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, | ||||
| 	struct hdac_stream *stream = NULL; | ||||
| 	struct hdac_bus *hbus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->ppcap) { | ||||
| 	if (!hbus->ppcap) { | ||||
| 		dev_err(hbus->dev, "stream type not supported\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| @ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, | ||||
| 	u32 register_mask = 0; | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->spbcap) { | ||||
| 	if (!bus->spbcap) { | ||||
| 		dev_err(bus->dev, "Address of SPB capability is NULL"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	mask |= (1 << index); | ||||
| 
 | ||||
| 	register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL); | ||||
| 	register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL); | ||||
| 
 | ||||
| 	mask |= register_mask; | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); | ||||
| 		snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); | ||||
| 	else | ||||
| 		snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); | ||||
| 		snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); | ||||
| 
 | ||||
| @ -452,7 +452,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, | ||||
| { | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->spbcap) { | ||||
| 	if (!bus->spbcap) { | ||||
| 		dev_err(bus->dev, "Address of SPB capability is NULL"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| @ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, | ||||
| { | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->spbcap) { | ||||
| 	if (!bus->spbcap) { | ||||
| 		dev_err(bus->dev, "Address of SPB capability is NULL"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| @ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, | ||||
| 	u32 register_mask = 0; | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->drsmcap) { | ||||
| 	if (!bus->drsmcap) { | ||||
| 		dev_err(bus->dev, "Address of DRSM capability is NULL"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	mask |= (1 << index); | ||||
| 
 | ||||
| 	register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL); | ||||
| 	register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL); | ||||
| 
 | ||||
| 	mask |= register_mask; | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask); | ||||
| 		snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask); | ||||
| 	else | ||||
| 		snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); | ||||
| 		snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); | ||||
| 
 | ||||
| @ -544,7 +544,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, | ||||
| { | ||||
| 	struct hdac_bus *bus = &ebus->bus; | ||||
| 
 | ||||
| 	if (!ebus->drsmcap) { | ||||
| 	if (!bus->drsmcap) { | ||||
| 		dev_err(bus->dev, "Address of DRSM capability is NULL"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| @ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); | ||||
| 
 | ||||
| #define HDAC_MAX_CAPS 10 | ||||
| /**
 | ||||
|  * snd_hdac_bus_parse_capabilities - parse capability structure | ||||
|  * @bus: the pointer to bus object | ||||
|  * | ||||
|  * Returns 0 if successful, or a negative error code. | ||||
|  */ | ||||
| int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus) | ||||
| { | ||||
| 	unsigned int cur_cap; | ||||
| 	unsigned int offset; | ||||
| 	unsigned int counter = 0; | ||||
| 
 | ||||
| 	offset = snd_hdac_chip_readl(bus, LLCH); | ||||
| 
 | ||||
| 	/* Lets walk the linked capabilities list */ | ||||
| 	do { | ||||
| 		cur_cap = _snd_hdac_chip_read(l, bus, offset); | ||||
| 
 | ||||
| 		dev_dbg(bus->dev, "Capability version: 0x%x\n", | ||||
| 			(cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF); | ||||
| 
 | ||||
| 		dev_dbg(bus->dev, "HDA capability ID: 0x%x\n", | ||||
| 			(cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF); | ||||
| 
 | ||||
| 		switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) { | ||||
| 		case AZX_ML_CAP_ID: | ||||
| 			dev_dbg(bus->dev, "Found ML capability\n"); | ||||
| 			bus->mlcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_GTS_CAP_ID: | ||||
| 			dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset); | ||||
| 			bus->gtscap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_PP_CAP_ID: | ||||
| 			/* PP capability found, the Audio DSP is present */ | ||||
| 			dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset); | ||||
| 			bus->ppcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_SPB_CAP_ID: | ||||
| 			/* SPIB capability found, handler function */ | ||||
| 			dev_dbg(bus->dev, "Found SPB capability\n"); | ||||
| 			bus->spbcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		case AZX_DRSM_CAP_ID: | ||||
| 			/* DMA resume  capability found, handler function */ | ||||
| 			dev_dbg(bus->dev, "Found DRSM capability\n"); | ||||
| 			bus->drsmcap = bus->remap_addr + offset; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		counter++; | ||||
| 
 | ||||
| 		if (counter > HDAC_MAX_CAPS) { | ||||
| 			dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n"); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		/* read the offset of next capability */ | ||||
| 		offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK; | ||||
| 
 | ||||
| 	} while (offset); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities); | ||||
| 
 | ||||
| /*
 | ||||
|  * Lowlevel interface | ||||
|  */ | ||||
|  | ||||
| @ -27,6 +27,12 @@ | ||||
| #include <linux/module.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #ifdef CONFIG_X86 | ||||
| /* for art-tsc conversion */ | ||||
| #include <asm/tsc.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <sound/core.h> | ||||
| #include <sound/initval.h> | ||||
| #include "hda_controller.h" | ||||
| @ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) | ||||
| 			       azx_get_position(chip, azx_dev)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * azx_scale64: Scale base by mult/div while not overflowing sanely | ||||
|  * | ||||
|  * Derived from scale64_check_overflow in kernel/time/timekeeping.c | ||||
|  * | ||||
|  * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which | ||||
|  * is about 384307 ie ~4.5 days. | ||||
|  * | ||||
|  * This scales the calculation so that overflow will happen but after 2^64 / | ||||
|  * 48000 secs, which is pretty large! | ||||
|  * | ||||
|  * In caln below: | ||||
|  *	base may overflow, but since there isn’t any additional division | ||||
|  *	performed on base it’s OK | ||||
|  *	rem can’t overflow because both are 32-bit values | ||||
|  */ | ||||
| 
 | ||||
| #ifdef CONFIG_X86 | ||||
| static u64 azx_scale64(u64 base, u32 num, u32 den) | ||||
| { | ||||
| 	u64 rem; | ||||
| 
 | ||||
| 	rem = do_div(base, den); | ||||
| 
 | ||||
| 	base *= num; | ||||
| 	rem *= num; | ||||
| 
 | ||||
| 	do_div(rem, den); | ||||
| 
 | ||||
| 	return base + rem; | ||||
| } | ||||
| 
 | ||||
| static int azx_get_sync_time(ktime_t *device, | ||||
| 		struct system_counterval_t *system, void *ctx) | ||||
| { | ||||
| 	struct snd_pcm_substream *substream = ctx; | ||||
| 	struct azx_dev *azx_dev = get_azx_dev(substream); | ||||
| 	struct azx_pcm *apcm = snd_pcm_substream_chip(substream); | ||||
| 	struct azx *chip = apcm->chip; | ||||
| 	struct snd_pcm_runtime *runtime; | ||||
| 	u64 ll_counter, ll_counter_l, ll_counter_h; | ||||
| 	u64 tsc_counter, tsc_counter_l, tsc_counter_h; | ||||
| 	u32 wallclk_ctr, wallclk_cycles; | ||||
| 	bool direction; | ||||
| 	u32 dma_select; | ||||
| 	u32 timeout = 200; | ||||
| 	u32 retry_count = 0; | ||||
| 
 | ||||
| 	runtime = substream->runtime; | ||||
| 
 | ||||
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||||
| 		direction = 1; | ||||
| 	else | ||||
| 		direction = 0; | ||||
| 
 | ||||
| 	/* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */ | ||||
| 	do { | ||||
| 		timeout = 100; | ||||
| 		dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) | | ||||
| 					(azx_dev->core.stream_tag - 1); | ||||
| 		snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select); | ||||
| 
 | ||||
| 		/* Enable the capture */ | ||||
| 		snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK); | ||||
| 
 | ||||
| 		while (timeout) { | ||||
| 			if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) & | ||||
| 						GTSCC_TSCCD_MASK) | ||||
| 				break; | ||||
| 
 | ||||
| 			timeout--; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!timeout) { | ||||
| 			dev_err(chip->card->dev, "GTSCC capture Timedout!\n"); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Read wall clock counter */ | ||||
| 		wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC); | ||||
| 
 | ||||
| 		/* Read TSC counter */ | ||||
| 		tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL); | ||||
| 		tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU); | ||||
| 
 | ||||
| 		/* Read Link counter */ | ||||
| 		ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL); | ||||
| 		ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU); | ||||
| 
 | ||||
| 		/* Ack: registers read done */ | ||||
| 		snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT); | ||||
| 
 | ||||
| 		tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) | | ||||
| 						tsc_counter_l; | ||||
| 
 | ||||
| 		ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) |	ll_counter_l; | ||||
| 		wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * An error occurs near frame "rollover". The clocks in | ||||
| 		 * frame value indicates whether this error may have | ||||
| 		 * occurred. Here we use the value of 10 i.e., | ||||
| 		 * HDA_MAX_CYCLE_OFFSET | ||||
| 		 */ | ||||
| 		if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET | ||||
| 					&& wallclk_cycles > HDA_MAX_CYCLE_OFFSET) | ||||
| 			break; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Sleep before we read again, else we may again get | ||||
| 		 * value near to MAX_CYCLE. Try to sleep for different | ||||
| 		 * amount of time so we dont hit the same number again | ||||
| 		 */ | ||||
| 		udelay(retry_count++); | ||||
| 
 | ||||
| 	} while (retry_count != HDA_MAX_CYCLE_READ_RETRY); | ||||
| 
 | ||||
| 	if (retry_count == HDA_MAX_CYCLE_READ_RETRY) { | ||||
| 		dev_err_ratelimited(chip->card->dev, | ||||
| 			"Error in WALFCC cycle count\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	*device = ns_to_ktime(azx_scale64(ll_counter, | ||||
| 				NSEC_PER_SEC, runtime->rate)); | ||||
| 	*device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) / | ||||
| 			       ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate)); | ||||
| 
 | ||||
| 	*system = convert_art_to_tsc(tsc_counter); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| static int azx_get_sync_time(ktime_t *device, | ||||
| 		struct system_counterval_t *system, void *ctx) | ||||
| { | ||||
| 	return -ENXIO; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int azx_get_crosststamp(struct snd_pcm_substream *substream, | ||||
| 			      struct system_device_crosststamp *xtstamp) | ||||
| { | ||||
| 	return get_device_system_crosststamp(azx_get_sync_time, | ||||
| 					substream, NULL, xtstamp); | ||||
| } | ||||
| 
 | ||||
| static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime, | ||||
| 				struct snd_pcm_audio_tstamp_config *ts) | ||||
| { | ||||
| 	if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME) | ||||
| 		if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED) | ||||
| 			return true; | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static int azx_get_time_info(struct snd_pcm_substream *substream, | ||||
| 			struct timespec *system_ts, struct timespec *audio_ts, | ||||
| 			struct snd_pcm_audio_tstamp_config *audio_tstamp_config, | ||||
| 			struct snd_pcm_audio_tstamp_report *audio_tstamp_report) | ||||
| { | ||||
| 	struct azx_dev *azx_dev = get_azx_dev(substream); | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct system_device_crosststamp xtstamp; | ||||
| 	int ret; | ||||
| 	u64 nsec; | ||||
| 
 | ||||
| 	if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && | ||||
| @ -361,8 +528,37 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, | ||||
| 		audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */ | ||||
| 		audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ | ||||
| 
 | ||||
| 	} else | ||||
| 	} else if (is_link_time_supported(runtime, audio_tstamp_config)) { | ||||
| 
 | ||||
| 		ret = azx_get_crosststamp(substream, &xtstamp); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		switch (runtime->tstamp_type) { | ||||
| 		case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC: | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: | ||||
| 			*system_ts = ktime_to_timespec(xtstamp.sys_monoraw); | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			*system_ts = ktime_to_timespec(xtstamp.sys_realtime); | ||||
| 			break; | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 		*audio_ts = ktime_to_timespec(xtstamp.device); | ||||
| 
 | ||||
| 		audio_tstamp_report->actual_type = | ||||
| 			SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED; | ||||
| 		audio_tstamp_report->accuracy_report = 1; | ||||
| 		/* 24 MHz WallClock == 42ns resolution */ | ||||
| 		audio_tstamp_report->accuracy = 42; | ||||
| 
 | ||||
| 	} else { | ||||
| 		audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 	runtime->private_data = azx_dev; | ||||
| 
 | ||||
| 	if (chip->gts_present) | ||||
| 		azx_pcm_hw.info = azx_pcm_hw.info | | ||||
| 			SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME; | ||||
| 
 | ||||
| 	runtime->hw = azx_pcm_hw; | ||||
| 	runtime->hw.channels_min = hinfo->channels_min; | ||||
| 	runtime->hw.channels_max = hinfo->channels_max; | ||||
|  | ||||
| @ -159,6 +159,9 @@ struct azx { | ||||
| 	unsigned int region_requested:1; | ||||
| 	unsigned int disabled:1; /* disabled by vga_switcheroo */ | ||||
| 
 | ||||
| 	/* GTS present */ | ||||
| 	unsigned int gts_present:1; | ||||
| 
 | ||||
| #ifdef CONFIG_SND_HDA_DSP_LOADER | ||||
| 	struct azx_dev saved_azx_dev; | ||||
| #endif | ||||
|  | ||||
| @ -54,6 +54,7 @@ | ||||
| /* for snoop control */ | ||||
| #include <asm/pgtable.h> | ||||
| #include <asm/cacheflush.h> | ||||
| #include <asm/cpufeature.h> | ||||
| #endif | ||||
| #include <sound/core.h> | ||||
| #include <sound/initval.h> | ||||
| @ -1663,6 +1664,22 @@ static int azx_first_init(struct azx *chip) | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (IS_SKL_PLUS(pci)) | ||||
| 		snd_hdac_bus_parse_capabilities(bus); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Some Intel CPUs has always running timer (ART) feature and | ||||
| 	 * controller may have Global time sync reporting capability, so | ||||
| 	 * check both of these before declaring synchronized time reporting | ||||
| 	 * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME | ||||
| 	 */ | ||||
| 	chip->gts_present = false; | ||||
| 
 | ||||
| #ifdef CONFIG_X86 | ||||
| 	if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART)) | ||||
| 		chip->gts_present = true; | ||||
| #endif | ||||
| 
 | ||||
| 	if (chip->msi) { | ||||
| 		if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { | ||||
| 			dev_dbg(card->dev, "Disabling 64bit MSI\n"); | ||||
|  | ||||
| @ -1870,6 +1870,9 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, | ||||
| 	case RT5640_SCLK_S_PLL1: | ||||
| 		reg_val |= RT5640_SCLK_SRC_PLL1; | ||||
| 		break; | ||||
| 	case RT5640_SCLK_S_RCCLK: | ||||
| 		reg_val |= RT5640_SCLK_SRC_RCCLK; | ||||
| 		break; | ||||
| 	default: | ||||
| 		dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); | ||||
| 		return -EINVAL; | ||||
|  | ||||
| @ -984,6 +984,7 @@ | ||||
| #define RT5640_SCLK_SRC_SFT			14 | ||||
| #define RT5640_SCLK_SRC_MCLK			(0x0 << 14) | ||||
| #define RT5640_SCLK_SRC_PLL1			(0x1 << 14) | ||||
| #define RT5640_SCLK_SRC_RCCLK			(0x2 << 14) | ||||
| #define RT5640_PLL1_SRC_MASK			(0x3 << 12) | ||||
| #define RT5640_PLL1_SRC_SFT			12 | ||||
| #define RT5640_PLL1_SRC_MCLK			(0x0 << 12) | ||||
|  | ||||
| @ -25,6 +25,7 @@ config SND_SST_IPC_ACPI | ||||
| 	tristate | ||||
| 	select SND_SST_IPC | ||||
| 	select SND_SOC_INTEL_SST | ||||
| 	select IOSF_MBI | ||||
| 
 | ||||
| config SND_SOC_INTEL_SST | ||||
| 	tristate | ||||
| @ -120,6 +121,17 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH | ||||
| 	  This adds audio driver for Intel Baytrail platform based boards | ||||
| 	  with the MAX98090 audio codec. | ||||
| 
 | ||||
| config SND_SOC_INTEL_BDW_RT5677_MACH | ||||
| 	tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" | ||||
| 	depends on X86_INTEL_LPSS && GPIOLIB && I2C && DW_DMAC | ||||
| 	depends on DW_DMAC_CORE=y | ||||
| 	select SND_SOC_INTEL_SST | ||||
| 	select SND_SOC_INTEL_HASWELL | ||||
| 	select SND_SOC_RT5677 | ||||
| 	help | ||||
| 	  This adds support for Intel Broadwell platform based boards with | ||||
| 	  the RT5677 audio codec. | ||||
| 
 | ||||
| config SND_SOC_INTEL_BROADWELL_MACH | ||||
| 	tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" | ||||
| 	depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| /*
 | ||||
|  /*
 | ||||
|  *  sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld | ||||
|  * | ||||
|  *  Copyright (C) 2013-14 Intel Corp | ||||
| @ -534,6 +534,7 @@ static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, | ||||
| 
 | ||||
| /* Look up table to convert MIXER SW bit regs to SWM inputs */ | ||||
| static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { | ||||
| 	[SST_IP_MODEM]		= SST_SWM_IN_MODEM, | ||||
| 	[SST_IP_CODEC0]		= SST_SWM_IN_CODEC0, | ||||
| 	[SST_IP_CODEC1]		= SST_SWM_IN_CODEC1, | ||||
| 	[SST_IP_LOOP0]		= SST_SWM_IN_SPROT_LOOP, | ||||
| @ -674,6 +675,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, | ||||
| /* SBA mixers - 16 inputs */ | ||||
| #define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)							\ | ||||
| 	static const struct snd_kcontrol_new kctl_name[] = {					\ | ||||
| 		SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0),		\ | ||||
| 		SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),		\ | ||||
| 		SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),		\ | ||||
| 		SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),	\ | ||||
| @ -684,6 +686,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, | ||||
| 	} | ||||
| 
 | ||||
| #define SST_SBA_MIXER_GRAPH_MAP(mix_name)			\ | ||||
| 	{ mix_name, "modem_in Switch",	"modem_in" },		\ | ||||
| 	{ mix_name, "codec_in0 Switch",	"codec_in0" },		\ | ||||
| 	{ mix_name, "codec_in1 Switch",	"codec_in1" },		\ | ||||
| 	{ mix_name, "sprot_loop_in Switch",	"sprot_loop_in" },	\ | ||||
| @ -713,6 +716,7 @@ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls); | ||||
| SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls); | ||||
| SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls); | ||||
| SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls); | ||||
| SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls); | ||||
| 
 | ||||
| /*
 | ||||
|  * sst_handle_vb_timer - Start/Stop the DSP scheduler | ||||
| @ -931,17 +935,26 @@ void sst_fill_ssp_defaults(struct snd_soc_dai *dai) | ||||
| int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) | ||||
| { | ||||
| 	struct sst_data *drv = snd_soc_dai_get_drvdata(dai); | ||||
| 	const struct sst_ssp_config *config; | ||||
| 	int ssp_id; | ||||
| 
 | ||||
| 	dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); | ||||
| 
 | ||||
| 	if (strcmp(id, "ssp0-port") == 0) | ||||
| 		ssp_id = SSP_MODEM; | ||||
| 	else if (strcmp(id, "ssp2-port") == 0) | ||||
| 		ssp_id = SSP_CODEC; | ||||
| 	else { | ||||
| 		dev_dbg(dai->dev, "port %s is not supported\n", id); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst); | ||||
| 	drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP; | ||||
| 	drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) | ||||
| 				- sizeof(struct sst_dsp_header); | ||||
| 
 | ||||
| 	config = &sst_ssp_configs; | ||||
| 	dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); | ||||
| 	drv->ssp_cmd.selection = ssp_id; | ||||
| 	dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id); | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		drv->ssp_cmd.switch_state = SST_SWITCH_ON; | ||||
| @ -1047,8 +1060,10 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w, | ||||
| } | ||||
| 
 | ||||
| static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { | ||||
| 	SST_AIF_IN("modem_in", sst_set_be_modules), | ||||
| 	SST_AIF_IN("codec_in0", sst_set_be_modules), | ||||
| 	SST_AIF_IN("codec_in1", sst_set_be_modules), | ||||
| 	SST_AIF_OUT("modem_out", sst_set_be_modules), | ||||
| 	SST_AIF_OUT("codec_out0", sst_set_be_modules), | ||||
| 	SST_AIF_OUT("codec_out1", sst_set_be_modules), | ||||
| 
 | ||||
| @ -1103,6 +1118,9 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { | ||||
| 		      sst_mix_codec0_controls, sst_swm_mixer_event), | ||||
| 	SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1, | ||||
| 		      sst_mix_codec1_controls, sst_swm_mixer_event), | ||||
| 	SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM, | ||||
| 		      sst_mix_modem_controls, sst_swm_mixer_event), | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route intercon[] = { | ||||
| @ -1148,6 +1166,9 @@ static const struct snd_soc_dapm_route intercon[] = { | ||||
| 	SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"), | ||||
| 	{"codec_out1", NULL, "codec_out1 mix 0"}, | ||||
| 	SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"), | ||||
| 	{"modem_out", NULL, "modem_out mix 0"}, | ||||
| 	SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"), | ||||
| 
 | ||||
| 
 | ||||
| }; | ||||
| static const char * const slot_names[] = { | ||||
| @ -1217,6 +1238,9 @@ static const struct snd_kcontrol_new sst_gain_controls[] = { | ||||
| 	SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]), | ||||
| 	SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]), | ||||
| 	SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]), | ||||
| 	SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]), | ||||
| 	SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]), | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #define SST_GAIN_NUM_CONTROLS 3 | ||||
|  | ||||
| @ -35,6 +35,8 @@ enum { | ||||
| /* define a bit for each mixer input */ | ||||
| #define SST_MIX_IP(x)		(x) | ||||
| 
 | ||||
| #define SST_IP_MODEM		SST_MIX_IP(0) | ||||
| #define SST_IP_BT		SST_MIX_IP(1) | ||||
| #define SST_IP_CODEC0		SST_MIX_IP(2) | ||||
| #define SST_IP_CODEC1		SST_MIX_IP(3) | ||||
| #define SST_IP_LOOP0		SST_MIX_IP(4) | ||||
| @ -63,6 +65,7 @@ enum { | ||||
|  * Audio DSP Path Ids. Specified by the audio DSP FW | ||||
|  */ | ||||
| enum sst_path_index { | ||||
| 	SST_PATH_INDEX_MODEM_OUT                = (0x00 << SST_PATH_ID_SHIFT), | ||||
| 	SST_PATH_INDEX_CODEC_OUT0               = (0x02 << SST_PATH_ID_SHIFT), | ||||
| 	SST_PATH_INDEX_CODEC_OUT1               = (0x03 << SST_PATH_ID_SHIFT), | ||||
| 
 | ||||
| @ -80,6 +83,7 @@ enum sst_path_index { | ||||
| 
 | ||||
| 
 | ||||
| 	/* Start of input paths */ | ||||
| 	SST_PATH_INDEX_MODEM_IN                 = (0x80 << SST_PATH_ID_SHIFT), | ||||
| 	SST_PATH_INDEX_CODEC_IN0                = (0x82 << SST_PATH_ID_SHIFT), | ||||
| 	SST_PATH_INDEX_CODEC_IN1                = (0x83 << SST_PATH_ID_SHIFT), | ||||
| 
 | ||||
| @ -105,6 +109,7 @@ enum sst_path_index { | ||||
|  * path IDs | ||||
|  */ | ||||
| enum sst_swm_inputs { | ||||
| 	SST_SWM_IN_MODEM	= (SST_PATH_INDEX_MODEM_IN	  | SST_DEFAULT_CELL_NBR), | ||||
| 	SST_SWM_IN_CODEC0	= (SST_PATH_INDEX_CODEC_IN0	  | SST_DEFAULT_CELL_NBR), | ||||
| 	SST_SWM_IN_CODEC1	= (SST_PATH_INDEX_CODEC_IN1	  | SST_DEFAULT_CELL_NBR), | ||||
| 	SST_SWM_IN_SPROT_LOOP	= (SST_PATH_INDEX_SPROT_LOOP_IN	  | SST_DEFAULT_CELL_NBR), | ||||
| @ -124,6 +129,7 @@ enum sst_swm_inputs { | ||||
|  * path IDs | ||||
|  */ | ||||
| enum sst_swm_outputs { | ||||
| 	SST_SWM_OUT_MODEM	= (SST_PATH_INDEX_MODEM_OUT	  | SST_DEFAULT_CELL_NBR), | ||||
| 	SST_SWM_OUT_CODEC0	= (SST_PATH_INDEX_CODEC_OUT0	  | SST_DEFAULT_CELL_NBR), | ||||
| 	SST_SWM_OUT_CODEC1	= (SST_PATH_INDEX_CODEC_OUT1	  | SST_DEFAULT_CELL_NBR), | ||||
| 	SST_SWM_OUT_SPROT_LOOP	= (SST_PATH_INDEX_SPROT_LOOP_OUT  | SST_DEFAULT_CELL_NBR), | ||||
|  | ||||
| @ -190,7 +190,8 @@ int sst_driver_ops(struct intel_sst_drv *sst) | ||||
| 
 | ||||
| 	default: | ||||
| 		dev_err(sst->dev, | ||||
| 			"SST Driver capablities missing for dev_id: %x", sst->dev_id); | ||||
| 			"SST Driver capabilities missing for dev_id: %x", | ||||
| 			sst->dev_id); | ||||
| 		return -EINVAL; | ||||
| 	}; | ||||
| } | ||||
| @ -441,7 +442,7 @@ static int intel_sst_suspend(struct device *dev) | ||||
| 		struct stream_info *stream = &ctx->streams[i]; | ||||
| 
 | ||||
| 		if (stream->status == STREAM_RUNNING) { | ||||
| 			dev_err(dev, "stream %d is running, cant susupend, abort\n", i); | ||||
| 			dev_err(dev, "stream %d is running, can't suspend, abort\n", i); | ||||
| 			return -EBUSY; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -39,6 +39,8 @@ | ||||
| #include <acpi/platform/aclinux.h> | ||||
| #include <acpi/actypes.h> | ||||
| #include <acpi/acpi_bus.h> | ||||
| #include <asm/cpu_device_id.h> | ||||
| #include <asm/iosf_mbi.h> | ||||
| #include "../sst-mfld-platform.h" | ||||
| #include "../../common/sst-dsp.h" | ||||
| #include "../../common/sst-acpi.h" | ||||
| @ -113,6 +115,28 @@ static const struct sst_res_info byt_rvp_res_info = { | ||||
| 	.acpi_ipc_irq_index = 5, | ||||
| }; | ||||
| 
 | ||||
| /* BYTCR has different BIOS from BYT */ | ||||
| static const struct sst_res_info bytcr_res_info = { | ||||
| 	.shim_offset = 0x140000, | ||||
| 	.shim_size = 0x000100, | ||||
| 	.shim_phy_addr = SST_BYT_SHIM_PHY_ADDR, | ||||
| 	.ssp0_offset = 0xa0000, | ||||
| 	.ssp0_size = 0x1000, | ||||
| 	.dma0_offset = 0x98000, | ||||
| 	.dma0_size = 0x4000, | ||||
| 	.dma1_offset = 0x9c000, | ||||
| 	.dma1_size = 0x4000, | ||||
| 	.iram_offset = 0x0c0000, | ||||
| 	.iram_size = 0x14000, | ||||
| 	.dram_offset = 0x100000, | ||||
| 	.dram_size = 0x28000, | ||||
| 	.mbox_offset = 0x144000, | ||||
| 	.mbox_size = 0x1000, | ||||
| 	.acpi_lpe_res_index = 0, | ||||
| 	.acpi_ddr_index = 2, | ||||
| 	.acpi_ipc_irq_index = 0 | ||||
| }; | ||||
| 
 | ||||
| static struct sst_platform_info byt_rvp_platform_data = { | ||||
| 	.probe_data = &byt_fwparse_info, | ||||
| 	.ipc_info = &byt_ipc_info, | ||||
| @ -142,7 +166,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	rsrc = platform_get_resource(pdev, IORESOURCE_MEM, | ||||
| 					ctx->pdata->res_info->acpi_lpe_res_index); | ||||
| 	if (!rsrc) { | ||||
| 		dev_err(ctx->dev, "Invalid SHIM base from IFWI"); | ||||
| 		dev_err(ctx->dev, "Invalid SHIM base from IFWI\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 	dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start, | ||||
| @ -154,7 +178,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, | ||||
| 					 ctx->pdata->res_info->iram_size); | ||||
| 	if (!ctx->iram) { | ||||
| 		dev_err(ctx->dev, "unable to map IRAM"); | ||||
| 		dev_err(ctx->dev, "unable to map IRAM\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| @ -164,7 +188,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, | ||||
| 					 ctx->pdata->res_info->dram_size); | ||||
| 	if (!ctx->dram) { | ||||
| 		dev_err(ctx->dev, "unable to map DRAM"); | ||||
| 		dev_err(ctx->dev, "unable to map DRAM\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| @ -173,7 +197,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, | ||||
| 					ctx->pdata->res_info->shim_size); | ||||
| 	if (!ctx->shim) { | ||||
| 		dev_err(ctx->dev, "unable to map SHIM"); | ||||
| 		dev_err(ctx->dev, "unable to map SHIM\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| @ -186,7 +210,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, | ||||
| 					    ctx->pdata->res_info->mbox_size); | ||||
| 	if (!ctx->mailbox) { | ||||
| 		dev_err(ctx->dev, "unable to map mailbox"); | ||||
| 		dev_err(ctx->dev, "unable to map mailbox\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| @ -196,7 +220,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	rsrc = platform_get_resource(pdev, IORESOURCE_MEM, | ||||
| 					ctx->pdata->res_info->acpi_ddr_index); | ||||
| 	if (!rsrc) { | ||||
| 		dev_err(ctx->dev, "Invalid DDR base from IFWI"); | ||||
| 		dev_err(ctx->dev, "Invalid DDR base from IFWI\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 	ctx->ddr_base = rsrc->start; | ||||
| @ -205,7 +229,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, | ||||
| 					resource_size(rsrc)); | ||||
| 	if (!ctx->ddr) { | ||||
| 		dev_err(ctx->dev, "unable to map DDR"); | ||||
| 		dev_err(ctx->dev, "unable to map DDR\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| @ -215,6 +239,46 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int is_byt_cr(struct device *dev, bool *bytcr) | ||||
| { | ||||
| 	int status = 0; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_IOSF_MBI)) { | ||||
| 		static const struct x86_cpu_id cpu_ids[] = { | ||||
| 			{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ | ||||
| 			{} | ||||
| 		}; | ||||
| 		u32 bios_status; | ||||
| 
 | ||||
| 		if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) { | ||||
| 			/* bail silently */ | ||||
| 			return status; | ||||
| 		} | ||||
| 
 | ||||
| 		status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */ | ||||
| 				       MBI_REG_READ, /* 0x10 */ | ||||
| 				       0x006, /* BIOS_CONFIG */ | ||||
| 				       &bios_status); | ||||
| 
 | ||||
| 		if (status) { | ||||
| 			dev_err(dev, "could not read PUNIT BIOS_CONFIG\n"); | ||||
| 		} else { | ||||
| 			/* bits 26:27 mirror PMIC options */ | ||||
| 			bios_status = (bios_status >> 26) & 3; | ||||
| 
 | ||||
| 			if ((bios_status == 1) || (bios_status == 3)) | ||||
| 				*bytcr = true; | ||||
| 			else | ||||
| 				dev_info(dev, "BYT-CR not detected\n"); | ||||
| 		} | ||||
| 	} else { | ||||
| 		dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n"); | ||||
| 	} | ||||
| 	return status; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int sst_acpi_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| @ -226,11 +290,12 @@ static int sst_acpi_probe(struct platform_device *pdev) | ||||
| 	struct platform_device *plat_dev; | ||||
| 	struct sst_platform_info *pdata; | ||||
| 	unsigned int dev_id; | ||||
| 	bool bytcr = false; | ||||
| 
 | ||||
| 	id = acpi_match_device(dev->driver->acpi_match_table, dev); | ||||
| 	if (!id) | ||||
| 		return -ENODEV; | ||||
| 	dev_dbg(dev, "for %s", id->id); | ||||
| 	dev_dbg(dev, "for %s\n", id->id); | ||||
| 
 | ||||
| 	mach = (struct sst_acpi_mach *)id->driver_data; | ||||
| 	mach = sst_acpi_find_machine(mach); | ||||
| @ -251,6 +316,18 @@ static int sst_acpi_probe(struct platform_device *pdev) | ||||
| 
 | ||||
| 	dev_dbg(dev, "ACPI device id: %x\n", dev_id); | ||||
| 
 | ||||
| 	ret = sst_alloc_drv_context(&ctx, dev, dev_id); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = is_byt_cr(dev, &bytcr); | ||||
| 	if (!((ret < 0) || (bytcr == false))) { | ||||
| 		dev_info(dev, "Detected Baytrail-CR platform\n"); | ||||
| 
 | ||||
| 		/* override resource info */ | ||||
| 		byt_rvp_platform_data.res_info = &bytcr_res_info; | ||||
| 	} | ||||
| 
 | ||||
| 	plat_dev = platform_device_register_data(dev, pdata->platform, -1, | ||||
| 						NULL, 0); | ||||
| 	if (IS_ERR(plat_dev)) { | ||||
| @ -271,10 +348,6 @@ static int sst_acpi_probe(struct platform_device *pdev) | ||||
| 		return PTR_ERR(mdev); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = sst_alloc_drv_context(&ctx, dev, dev_id); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Fill sst platform data */ | ||||
| 	ctx->pdata = pdata; | ||||
| 	strcpy(ctx->firmware_name, mach->fw_filename); | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| snd-soc-sst-haswell-objs := haswell.o | ||||
| snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o | ||||
| snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o | ||||
| snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o | ||||
| snd-soc-sst-broadwell-objs := broadwell.o | ||||
| snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o | ||||
| snd-soc-sst-bxt-rt298-objs := bxt_rt298.o | ||||
| @ -19,6 +20,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o | ||||
| obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o | ||||
|  | ||||
							
								
								
									
										347
									
								
								sound/soc/intel/boards/bdw-rt5677.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										347
									
								
								sound/soc/intel/boards/bdw-rt5677.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,347 @@ | ||||
| /*
 | ||||
|  * ASoC machine driver for Intel Broadwell platforms with RT5677 codec | ||||
|  * | ||||
|  * Copyright (c) 2014, The Chromium OS Authors.  All rights reserved. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms and conditions of the GNU General Public License, | ||||
|  * version 2, as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/gpio/consumer.h> | ||||
| #include <linux/delay.h> | ||||
| #include <sound/core.h> | ||||
| #include <sound/pcm.h> | ||||
| #include <sound/soc.h> | ||||
| #include <sound/pcm_params.h> | ||||
| #include <sound/jack.h> | ||||
| 
 | ||||
| #include "../common/sst-dsp.h" | ||||
| #include "../haswell/sst-haswell-ipc.h" | ||||
| 
 | ||||
| #include "../../codecs/rt5677.h" | ||||
| 
 | ||||
| struct bdw_rt5677_priv { | ||||
| 	struct gpio_desc *gpio_hp_en; | ||||
| 	struct snd_soc_codec *codec; | ||||
| }; | ||||
| 
 | ||||
| static int bdw_rt5677_event_hp(struct snd_soc_dapm_widget *w, | ||||
| 			struct snd_kcontrol *k, int event) | ||||
| { | ||||
| 	struct snd_soc_dapm_context *dapm = w->dapm; | ||||
| 	struct snd_soc_card *card = dapm->card; | ||||
| 	struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); | ||||
| 
 | ||||
| 	if (SND_SOC_DAPM_EVENT_ON(event)) | ||||
| 		msleep(70); | ||||
| 
 | ||||
| 	gpiod_set_value_cansleep(bdw_rt5677->gpio_hp_en, | ||||
| 		SND_SOC_DAPM_EVENT_ON(event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct snd_soc_dapm_widget bdw_rt5677_widgets[] = { | ||||
| 	SND_SOC_DAPM_HP("Headphone", bdw_rt5677_event_hp), | ||||
| 	SND_SOC_DAPM_SPK("Speaker", NULL), | ||||
| 	SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||||
| 	SND_SOC_DAPM_MIC("Local DMICs", NULL), | ||||
| 	SND_SOC_DAPM_MIC("Remote DMICs", NULL), | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route bdw_rt5677_map[] = { | ||||
| 	/* Speakers */ | ||||
| 	{"Speaker", NULL, "PDM1L"}, | ||||
| 	{"Speaker", NULL, "PDM1R"}, | ||||
| 
 | ||||
| 	/* Headset jack connectors */ | ||||
| 	{"Headphone", NULL, "LOUT1"}, | ||||
| 	{"Headphone", NULL, "LOUT2"}, | ||||
| 	{"IN1P", NULL, "Headset Mic"}, | ||||
| 	{"IN1N", NULL, "Headset Mic"}, | ||||
| 
 | ||||
| 	/* Digital MICs
 | ||||
| 	 * Local DMICs: the two DMICs on the mainboard | ||||
| 	 * Remote DMICs: the two DMICs on the camera module | ||||
| 	 */ | ||||
| 	{"DMIC L1", NULL, "Remote DMICs"}, | ||||
| 	{"DMIC R1", NULL, "Remote DMICs"}, | ||||
| 	{"DMIC L2", NULL, "Local DMICs"}, | ||||
| 	{"DMIC R2", NULL, "Local DMICs"}, | ||||
| 
 | ||||
| 	/* CODEC BE connections */ | ||||
| 	{"SSP0 CODEC IN", NULL, "AIF1 Capture"}, | ||||
| 	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_kcontrol_new bdw_rt5677_controls[] = { | ||||
| 	SOC_DAPM_PIN_SWITCH("Speaker"), | ||||
| 	SOC_DAPM_PIN_SWITCH("Headphone"), | ||||
| 	SOC_DAPM_PIN_SWITCH("Headset Mic"), | ||||
| 	SOC_DAPM_PIN_SWITCH("Local DMICs"), | ||||
| 	SOC_DAPM_PIN_SWITCH("Remote DMICs"), | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static struct snd_soc_jack headphone_jack; | ||||
| static struct snd_soc_jack mic_jack; | ||||
| 
 | ||||
| static struct snd_soc_jack_pin headphone_jack_pin = { | ||||
| 	.pin	= "Headphone", | ||||
| 	.mask	= SND_JACK_HEADPHONE, | ||||
| }; | ||||
| 
 | ||||
| static struct snd_soc_jack_pin mic_jack_pin = { | ||||
| 	.pin	= "Headset Mic", | ||||
| 	.mask	= SND_JACK_MICROPHONE, | ||||
| }; | ||||
| 
 | ||||
| static struct snd_soc_jack_gpio headphone_jack_gpio = { | ||||
| 	.name			= "plug-det", | ||||
| 	.report			= SND_JACK_HEADPHONE, | ||||
| 	.debounce_time		= 200, | ||||
| }; | ||||
| 
 | ||||
| static struct snd_soc_jack_gpio mic_jack_gpio = { | ||||
| 	.name			= "mic-present", | ||||
| 	.report			= SND_JACK_MICROPHONE, | ||||
| 	.debounce_time		= 200, | ||||
| 	.invert			= 1, | ||||
| }; | ||||
| 
 | ||||
| static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, | ||||
| 			struct snd_pcm_hw_params *params) | ||||
| { | ||||
| 	struct snd_interval *rate = hw_param_interval(params, | ||||
| 			SNDRV_PCM_HW_PARAM_RATE); | ||||
| 	struct snd_interval *channels = hw_param_interval(params, | ||||
| 						SNDRV_PCM_HW_PARAM_CHANNELS); | ||||
| 
 | ||||
| 	/* The ADSP will covert the FE rate to 48k, stereo */ | ||||
| 	rate->min = rate->max = 48000; | ||||
| 	channels->min = channels->max = 2; | ||||
| 
 | ||||
| 	/* set SSP0 to 16 bit */ | ||||
| 	snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - | ||||
| 				    SNDRV_PCM_HW_PARAM_FIRST_MASK], | ||||
| 				    SNDRV_PCM_FORMAT_S16_LE); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream, | ||||
| 	struct snd_pcm_hw_params *params) | ||||
| { | ||||
| 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||||
| 	struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, 24576000, | ||||
| 		SND_SOC_CLOCK_IN); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(rtd->dev, "can't set codec sysclk configuration\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct snd_soc_ops bdw_rt5677_ops = { | ||||
| 	.hw_params = bdw_rt5677_hw_params, | ||||
| }; | ||||
| 
 | ||||
| static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) | ||||
| { | ||||
| 	struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev); | ||||
| 	struct sst_hsw *broadwell = pdata->dsp; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* Set ADSP SSP port settings */ | ||||
| 	ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, | ||||
| 		SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, | ||||
| 		SST_HSW_DEVICE_CLOCK_MASTER, 9); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(rtd->dev, "error: failed to set device config\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) | ||||
| { | ||||
| 	struct bdw_rt5677_priv *bdw_rt5677 = | ||||
| 			snd_soc_card_get_drvdata(rtd->card); | ||||
| 	struct snd_soc_codec *codec = rtd->codec; | ||||
| 	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); | ||||
| 
 | ||||
| 	/* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
 | ||||
| 	 * The ASRC clock source is clk_i2s1_asrc. | ||||
| 	 */ | ||||
| 	rt5677_sel_asrc_clk_src(codec, RT5677_DA_STEREO_FILTER | | ||||
| 			RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE, | ||||
| 			RT5677_CLK_SEL_I2S1_ASRC); | ||||
| 
 | ||||
| 	/* Request rt5677 GPIO for headphone amp control */ | ||||
| 	bdw_rt5677->gpio_hp_en = devm_gpiod_get_index(codec->dev, | ||||
| 			"headphone-enable", 0, 0); | ||||
| 	if (IS_ERR(bdw_rt5677->gpio_hp_en)) { | ||||
| 		dev_err(codec->dev, "Can't find HP_AMP_SHDN_L gpio\n"); | ||||
| 		return PTR_ERR(bdw_rt5677->gpio_hp_en); | ||||
| 	} | ||||
| 	gpiod_direction_output(bdw_rt5677->gpio_hp_en, 0); | ||||
| 
 | ||||
| 	/* Create and initialize headphone jack */ | ||||
| 	if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack", | ||||
| 			SND_JACK_HEADPHONE, &headphone_jack, | ||||
| 			&headphone_jack_pin, 1)) { | ||||
| 		headphone_jack_gpio.gpiod_dev = codec->dev; | ||||
| 		if (snd_soc_jack_add_gpios(&headphone_jack, 1, | ||||
| 				&headphone_jack_gpio)) | ||||
| 			dev_err(codec->dev, "Can't add headphone jack gpio\n"); | ||||
| 	} else { | ||||
| 		dev_err(codec->dev, "Can't create headphone jack\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Create and initialize mic jack */ | ||||
| 	if (!snd_soc_card_jack_new(rtd->card, "Mic Jack", | ||||
| 			SND_JACK_MICROPHONE, &mic_jack, | ||||
| 			&mic_jack_pin, 1)) { | ||||
| 		mic_jack_gpio.gpiod_dev = codec->dev; | ||||
| 		if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio)) | ||||
| 			dev_err(codec->dev, "Can't add mic jack gpio\n"); | ||||
| 	} else { | ||||
| 		dev_err(codec->dev, "Can't create mic jack\n"); | ||||
| 	} | ||||
| 	bdw_rt5677->codec = codec; | ||||
| 
 | ||||
| 	snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* broadwell digital audio interface glue - connects codec <--> CPU */ | ||||
| static struct snd_soc_dai_link bdw_rt5677_dais[] = { | ||||
| 	/* Front End DAI links */ | ||||
| 	{ | ||||
| 		.name = "System PCM", | ||||
| 		.stream_name = "System Playback/Capture", | ||||
| 		.cpu_dai_name = "System Pin", | ||||
| 		.platform_name = "haswell-pcm-audio", | ||||
| 		.dynamic = 1, | ||||
| 		.codec_name = "snd-soc-dummy", | ||||
| 		.codec_dai_name = "snd-soc-dummy-dai", | ||||
| 		.init = bdw_rt5677_rtd_init, | ||||
| 		.trigger = { | ||||
| 			SND_SOC_DPCM_TRIGGER_POST, | ||||
| 			SND_SOC_DPCM_TRIGGER_POST | ||||
| 		}, | ||||
| 		.dpcm_capture = 1, | ||||
| 		.dpcm_playback = 1, | ||||
| 	}, | ||||
| 
 | ||||
| 	/* Back End DAI links */ | ||||
| 	{ | ||||
| 		/* SSP0 - Codec */ | ||||
| 		.name = "Codec", | ||||
| 		.id = 0, | ||||
| 		.cpu_dai_name = "snd-soc-dummy-dai", | ||||
| 		.platform_name = "snd-soc-dummy", | ||||
| 		.no_pcm = 1, | ||||
| 		.codec_name = "i2c-RT5677CE:00", | ||||
| 		.codec_dai_name = "rt5677-aif1", | ||||
| 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||||
| 			SND_SOC_DAIFMT_CBS_CFS, | ||||
| 		.ignore_suspend = 1, | ||||
| 		.ignore_pmdown_time = 1, | ||||
| 		.be_hw_params_fixup = broadwell_ssp0_fixup, | ||||
| 		.ops = &bdw_rt5677_ops, | ||||
| 		.dpcm_playback = 1, | ||||
| 		.dpcm_capture = 1, | ||||
| 		.init = bdw_rt5677_init, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int bdw_rt5677_suspend_pre(struct snd_soc_card *card) | ||||
| { | ||||
| 	struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); | ||||
| 	struct snd_soc_dapm_context *dapm; | ||||
| 
 | ||||
| 	if (bdw_rt5677->codec) { | ||||
| 		dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec); | ||||
| 		snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int bdw_rt5677_resume_post(struct snd_soc_card *card) | ||||
| { | ||||
| 	struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); | ||||
| 	struct snd_soc_dapm_context *dapm; | ||||
| 
 | ||||
| 	if (bdw_rt5677->codec) { | ||||
| 		dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec); | ||||
| 		snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* ASoC machine driver for Broadwell DSP + RT5677 */ | ||||
| static struct snd_soc_card bdw_rt5677_card = { | ||||
| 	.name = "bdw-rt5677", | ||||
| 	.owner = THIS_MODULE, | ||||
| 	.dai_link = bdw_rt5677_dais, | ||||
| 	.num_links = ARRAY_SIZE(bdw_rt5677_dais), | ||||
| 	.dapm_widgets = bdw_rt5677_widgets, | ||||
| 	.num_dapm_widgets = ARRAY_SIZE(bdw_rt5677_widgets), | ||||
| 	.dapm_routes = bdw_rt5677_map, | ||||
| 	.num_dapm_routes = ARRAY_SIZE(bdw_rt5677_map), | ||||
| 	.controls = bdw_rt5677_controls, | ||||
| 	.num_controls = ARRAY_SIZE(bdw_rt5677_controls), | ||||
| 	.fully_routed = true, | ||||
| 	.suspend_pre = bdw_rt5677_suspend_pre, | ||||
| 	.resume_post = bdw_rt5677_resume_post, | ||||
| }; | ||||
| 
 | ||||
| static int bdw_rt5677_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct bdw_rt5677_priv *bdw_rt5677; | ||||
| 
 | ||||
| 	bdw_rt5677_card.dev = &pdev->dev; | ||||
| 
 | ||||
| 	/* Allocate driver private struct */ | ||||
| 	bdw_rt5677 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5677_priv), | ||||
| 		GFP_KERNEL); | ||||
| 	if (!bdw_rt5677) { | ||||
| 		dev_err(&pdev->dev, "Can't allocate bdw_rt5677\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	snd_soc_card_set_drvdata(&bdw_rt5677_card, bdw_rt5677); | ||||
| 
 | ||||
| 	return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5677_card); | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver bdw_rt5677_audio = { | ||||
| 	.probe = bdw_rt5677_probe, | ||||
| 	.driver = { | ||||
| 		.name = "bdw-rt5677", | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| module_platform_driver(bdw_rt5677_audio) | ||||
| 
 | ||||
| /* Module information */ | ||||
| MODULE_AUTHOR("Ben Zhang"); | ||||
| MODULE_DESCRIPTION("Intel Broadwell RT5677 machine driver"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| MODULE_ALIAS("platform:bdw-rt5677"); | ||||
| @ -37,6 +37,7 @@ enum { | ||||
| 	BXT_DPCM_AUDIO_PB = 0, | ||||
| 	BXT_DPCM_AUDIO_CP, | ||||
| 	BXT_DPCM_AUDIO_REF_CP, | ||||
| 	BXT_DPCM_AUDIO_DMIC_CP, | ||||
| 	BXT_DPCM_AUDIO_HDMI1_PB, | ||||
| 	BXT_DPCM_AUDIO_HDMI2_PB, | ||||
| 	BXT_DPCM_AUDIO_HDMI3_PB, | ||||
| @ -252,10 +253,56 @@ static struct snd_soc_ops broxton_da7219_ops = { | ||||
| 	.hw_free = broxton_da7219_hw_free, | ||||
| }; | ||||
| 
 | ||||
| static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, | ||||
| 			struct snd_pcm_hw_params *params) | ||||
| { | ||||
| 	struct snd_interval *channels = hw_param_interval(params, | ||||
| 						SNDRV_PCM_HW_PARAM_CHANNELS); | ||||
| 	channels->min = channels->max = DUAL_CHANNEL; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int broxton_dmic_startup(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 
 | ||||
| 	runtime->hw.channels_max = DUAL_CHANNEL; | ||||
| 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | ||||
| 			&constraints_channels); | ||||
| 
 | ||||
| 	return snd_pcm_hw_constraint_list(substream->runtime, 0, | ||||
| 			SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); | ||||
| } | ||||
| 
 | ||||
| static const struct snd_soc_ops broxton_dmic_ops = { | ||||
| 	.startup = broxton_dmic_startup, | ||||
| }; | ||||
| 
 | ||||
| static const unsigned int rates_16000[] = { | ||||
| 	16000, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_pcm_hw_constraint_list constraints_16000 = { | ||||
| 	.count = ARRAY_SIZE(rates_16000), | ||||
| 	.list  = rates_16000, | ||||
| }; | ||||
| 
 | ||||
| static int broxton_refcap_startup(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	return snd_pcm_hw_constraint_list(substream->runtime, 0, | ||||
| 			SNDRV_PCM_HW_PARAM_RATE, | ||||
| 			&constraints_16000); | ||||
| }; | ||||
| 
 | ||||
| static struct snd_soc_ops broxton_refcap_ops = { | ||||
| 	.startup = broxton_refcap_startup, | ||||
| }; | ||||
| 
 | ||||
| /* broxton digital audio interface glue - connects codec <--> CPU */ | ||||
| static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 	/* Front End DAI links */ | ||||
| 	[BXT_DPCM_AUDIO_PB] | ||||
| 	[BXT_DPCM_AUDIO_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio Port", | ||||
| 		.stream_name = "Audio", | ||||
| @ -271,7 +318,7 @@ static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 		.dpcm_playback = 1, | ||||
| 		.ops = &broxton_da7219_fe_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_CP] | ||||
| 	[BXT_DPCM_AUDIO_CP] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio Capture Port", | ||||
| 		.stream_name = "Audio Record", | ||||
| @ -286,7 +333,7 @@ static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 		.dpcm_capture = 1, | ||||
| 		.ops = &broxton_da7219_fe_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_REF_CP] | ||||
| 	[BXT_DPCM_AUDIO_REF_CP] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio Reference cap", | ||||
| 		.stream_name = "Refcap", | ||||
| @ -299,8 +346,23 @@ static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 		.ignore_suspend = 1, | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 		.ops = &broxton_refcap_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI1_PB] | ||||
| 	[BXT_DPCM_AUDIO_DMIC_CP] | ||||
| 	{ | ||||
| 		.name = "Bxt Audio DMIC cap", | ||||
| 		.stream_name = "dmiccap", | ||||
| 		.cpu_dai_name = "DMIC Pin", | ||||
| 		.codec_name = "snd-soc-dummy", | ||||
| 		.codec_dai_name = "snd-soc-dummy-dai", | ||||
| 		.platform_name = "0000:00:0e.0", | ||||
| 		.init = NULL, | ||||
| 		.dpcm_capture = 1, | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 		.ops = &broxton_dmic_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI1_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt HDMI Port1", | ||||
| 		.stream_name = "Hdmi1", | ||||
| @ -313,7 +375,7 @@ static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI2_PB] | ||||
| 	[BXT_DPCM_AUDIO_HDMI2_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt HDMI Port2", | ||||
| 		.stream_name = "Hdmi2", | ||||
| @ -326,7 +388,7 @@ static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI3_PB] | ||||
| 	[BXT_DPCM_AUDIO_HDMI3_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt HDMI Port3", | ||||
| 		.stream_name = "Hdmi3", | ||||
| @ -382,6 +444,7 @@ static struct snd_soc_dai_link broxton_dais[] = { | ||||
| 		.codec_dai_name = "dmic-hifi", | ||||
| 		.platform_name = "0000:00:0e.0", | ||||
| 		.ignore_suspend = 1, | ||||
| 		.be_hw_params_fixup = broxton_dmic_fixup, | ||||
| 		.dpcm_capture = 1, | ||||
| 		.no_pcm = 1, | ||||
| 	}, | ||||
|  | ||||
| @ -271,7 +271,7 @@ static const struct snd_soc_ops broxton_rt286_fe_ops = { | ||||
| /* broxton digital audio interface glue - connects codec <--> CPU */ | ||||
| static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 	/* Front End DAI links */ | ||||
| 	[BXT_DPCM_AUDIO_PB] | ||||
| 	[BXT_DPCM_AUDIO_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio Port", | ||||
| 		.stream_name = "Audio", | ||||
| @ -286,7 +286,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 		.dpcm_playback = 1, | ||||
| 		.ops = &broxton_rt286_fe_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_CP] | ||||
| 	[BXT_DPCM_AUDIO_CP] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio Capture Port", | ||||
| 		.stream_name = "Audio Record", | ||||
| @ -300,7 +300,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 		.dpcm_capture = 1, | ||||
| 		.ops = &broxton_rt286_fe_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_REF_CP] | ||||
| 	[BXT_DPCM_AUDIO_REF_CP] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio Reference cap", | ||||
| 		.stream_name = "refcap", | ||||
| @ -313,7 +313,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_DMIC_CP] | ||||
| 	[BXT_DPCM_AUDIO_DMIC_CP] = | ||||
| 	{ | ||||
| 		.name = "Bxt Audio DMIC cap", | ||||
| 		.stream_name = "dmiccap", | ||||
| @ -327,7 +327,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 		.dynamic = 1, | ||||
| 		.ops = &broxton_dmic_ops, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI1_PB] | ||||
| 	[BXT_DPCM_AUDIO_HDMI1_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt HDMI Port1", | ||||
| 		.stream_name = "Hdmi1", | ||||
| @ -340,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI2_PB] | ||||
| 	[BXT_DPCM_AUDIO_HDMI2_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt HDMI Port2", | ||||
| 		.stream_name = "Hdmi2", | ||||
| @ -353,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { | ||||
| 		.nonatomic = 1, | ||||
| 		.dynamic = 1, | ||||
| 	}, | ||||
| 	[BXT_DPCM_AUDIO_HDMI3_PB] | ||||
| 	[BXT_DPCM_AUDIO_HDMI3_PB] = | ||||
| 	{ | ||||
| 		.name = "Bxt HDMI Port3", | ||||
| 		.stream_name = "Hdmi3", | ||||
|  | ||||
| @ -24,6 +24,9 @@ | ||||
| #include <linux/device.h> | ||||
| #include <linux/dmi.h> | ||||
| #include <linux/slab.h> | ||||
| #include <asm/cpu_device_id.h> | ||||
| #include <asm/platform_sst_audio.h> | ||||
| #include <linux/clk.h> | ||||
| #include <sound/pcm.h> | ||||
| #include <sound/pcm_params.h> | ||||
| #include <sound/soc.h> | ||||
| @ -31,42 +34,153 @@ | ||||
| #include "../../codecs/rt5640.h" | ||||
| #include "../atom/sst-atom-controls.h" | ||||
| #include "../common/sst-acpi.h" | ||||
| #include "../common/sst-dsp.h" | ||||
| 
 | ||||
| enum { | ||||
| 	BYT_RT5640_DMIC1_MAP, | ||||
| 	BYT_RT5640_DMIC2_MAP, | ||||
| 	BYT_RT5640_IN1_MAP, | ||||
| 	BYT_RT5640_IN3_MAP, | ||||
| }; | ||||
| 
 | ||||
| #define BYT_RT5640_MAP(quirk)	((quirk) & 0xff) | ||||
| #define BYT_RT5640_DMIC_EN	BIT(16) | ||||
| #define BYT_RT5640_MONO_SPEAKER BIT(17) | ||||
| #define BYT_RT5640_DIFF_MIC     BIT(18) /* defaut is single-ended */ | ||||
| #define BYT_RT5640_SSP2_AIF2     BIT(19) /* default is using AIF1  */ | ||||
| #define BYT_RT5640_SSP0_AIF1     BIT(20) | ||||
| #define BYT_RT5640_SSP0_AIF2     BIT(21) | ||||
| #define BYT_RT5640_MCLK_EN	BIT(22) | ||||
| #define BYT_RT5640_MCLK_25MHZ	BIT(23) | ||||
| 
 | ||||
| struct byt_rt5640_private { | ||||
| 	struct clk *mclk; | ||||
| }; | ||||
| 
 | ||||
| static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | | ||||
| 					BYT_RT5640_DMIC_EN; | ||||
| 					BYT_RT5640_DMIC_EN | | ||||
| 					BYT_RT5640_MCLK_EN; | ||||
| 
 | ||||
| static void log_quirks(struct device *dev) | ||||
| { | ||||
| 	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC1_MAP) | ||||
| 		dev_info(dev, "quirk DMIC1_MAP enabled"); | ||||
| 	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC2_MAP) | ||||
| 		dev_info(dev, "quirk DMIC2_MAP enabled"); | ||||
| 	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN1_MAP) | ||||
| 		dev_info(dev, "quirk IN1_MAP enabled"); | ||||
| 	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN3_MAP) | ||||
| 		dev_info(dev, "quirk IN3_MAP enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) | ||||
| 		dev_info(dev, "quirk DMIC enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) | ||||
| 		dev_info(dev, "quirk MONO_SPEAKER enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) | ||||
| 		dev_info(dev, "quirk DIFF_MIC enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) | ||||
| 		dev_info(dev, "quirk SSP2_AIF2 enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) | ||||
| 		dev_info(dev, "quirk SSP0_AIF1 enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) | ||||
| 		dev_info(dev, "quirk SSP0_AIF2 enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) | ||||
| 		dev_info(dev, "quirk MCLK_EN enabled"); | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) | ||||
| 		dev_info(dev, "quirk MCLK_25MHZ enabled"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #define BYT_CODEC_DAI1	"rt5640-aif1" | ||||
| #define BYT_CODEC_DAI2	"rt5640-aif2" | ||||
| 
 | ||||
| static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card) | ||||
| { | ||||
| 	struct snd_soc_pcm_runtime *rtd; | ||||
| 
 | ||||
| 	list_for_each_entry(rtd, &card->rtd_list, list) { | ||||
| 		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1, | ||||
| 			     strlen(BYT_CODEC_DAI1))) | ||||
| 			return rtd->codec_dai; | ||||
| 		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2, | ||||
| 				strlen(BYT_CODEC_DAI2))) | ||||
| 			return rtd->codec_dai; | ||||
| 
 | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static int platform_clock_control(struct snd_soc_dapm_widget *w, | ||||
| 				  struct snd_kcontrol *k, int  event) | ||||
| { | ||||
| 	struct snd_soc_dapm_context *dapm = w->dapm; | ||||
| 	struct snd_soc_card *card = dapm->card; | ||||
| 	struct snd_soc_dai *codec_dai; | ||||
| 	struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	codec_dai = byt_get_codec_dai(card); | ||||
| 	if (!codec_dai) { | ||||
| 		dev_err(card->dev, | ||||
| 			"Codec dai not found; Unable to set platform clock\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (SND_SOC_DAPM_EVENT_ON(event)) { | ||||
| 		if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { | ||||
| 			ret = clk_prepare_enable(priv->mclk); | ||||
| 			if (ret < 0) { | ||||
| 				dev_err(card->dev, | ||||
| 					"could not configure MCLK state"); | ||||
| 				return ret; | ||||
| 			} | ||||
| 		} | ||||
| 		ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, | ||||
| 					     48000 * 512, | ||||
| 					     SND_SOC_CLOCK_IN); | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * Set codec clock source to internal clock before | ||||
| 		 * turning off the platform clock. Codec needs clock | ||||
| 		 * for Jack detection and button press | ||||
| 		 */ | ||||
| 		ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK, | ||||
| 					     0, | ||||
| 					     SND_SOC_CLOCK_IN); | ||||
| 		if (!ret) { | ||||
| 			if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) | ||||
| 				clk_disable_unprepare(priv->mclk); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(card->dev, "can't set codec sysclk: %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { | ||||
| 	SND_SOC_DAPM_HP("Headphone", NULL), | ||||
| 	SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||||
| 	SND_SOC_DAPM_MIC("Internal Mic", NULL), | ||||
| 	SND_SOC_DAPM_SPK("Speaker", NULL), | ||||
| 	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, | ||||
| 			    platform_clock_control, SND_SOC_DAPM_PRE_PMU | | ||||
| 			    SND_SOC_DAPM_POST_PMD), | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { | ||||
| 	{"AIF1 Playback", NULL, "ssp2 Tx"}, | ||||
| 	{"ssp2 Tx", NULL, "codec_out0"}, | ||||
| 	{"ssp2 Tx", NULL, "codec_out1"}, | ||||
| 	{"codec_in0", NULL, "ssp2 Rx"}, | ||||
| 	{"codec_in1", NULL, "ssp2 Rx"}, | ||||
| 	{"ssp2 Rx", NULL, "AIF1 Capture"}, | ||||
| 	{"Headphone", NULL, "Platform Clock"}, | ||||
| 	{"Headset Mic", NULL, "Platform Clock"}, | ||||
| 	{"Internal Mic", NULL, "Platform Clock"}, | ||||
| 	{"Speaker", NULL, "Platform Clock"}, | ||||
| 
 | ||||
| 	{"Headset Mic", NULL, "MICBIAS1"}, | ||||
| 	{"IN2P", NULL, "Headset Mic"}, | ||||
| 	{"Headphone", NULL, "HPOL"}, | ||||
| 	{"Headphone", NULL, "HPOR"}, | ||||
| 	{"Speaker", NULL, "SPOLP"}, | ||||
| 	{"Speaker", NULL, "SPOLN"}, | ||||
| 	{"Speaker", NULL, "SPORP"}, | ||||
| 	{"Speaker", NULL, "SPORN"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { | ||||
| @ -82,6 +196,59 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { | ||||
| 	{"IN1P", NULL, "Internal Mic"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = { | ||||
| 	{"Internal Mic", NULL, "MICBIAS1"}, | ||||
| 	{"IN3P", NULL, "Internal Mic"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif1_map[] = { | ||||
| 	{"ssp2 Tx", NULL, "codec_out0"}, | ||||
| 	{"ssp2 Tx", NULL, "codec_out1"}, | ||||
| 	{"codec_in0", NULL, "ssp2 Rx"}, | ||||
| 	{"codec_in1", NULL, "ssp2 Rx"}, | ||||
| 
 | ||||
| 	{"AIF1 Playback", NULL, "ssp2 Tx"}, | ||||
| 	{"ssp2 Rx", NULL, "AIF1 Capture"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif2_map[] = { | ||||
| 	{"ssp2 Tx", NULL, "codec_out0"}, | ||||
| 	{"ssp2 Tx", NULL, "codec_out1"}, | ||||
| 	{"codec_in0", NULL, "ssp2 Rx"}, | ||||
| 	{"codec_in1", NULL, "ssp2 Rx"}, | ||||
| 
 | ||||
| 	{"AIF2 Playback", NULL, "ssp2 Tx"}, | ||||
| 	{"ssp2 Rx", NULL, "AIF2 Capture"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif1_map[] = { | ||||
| 	{"ssp0 Tx", NULL, "modem_out"}, | ||||
| 	{"modem_in", NULL, "ssp0 Rx"}, | ||||
| 
 | ||||
| 	{"AIF1 Playback", NULL, "ssp0 Tx"}, | ||||
| 	{"ssp0 Rx", NULL, "AIF1 Capture"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif2_map[] = { | ||||
| 	{"ssp0 Tx", NULL, "modem_out"}, | ||||
| 	{"modem_in", NULL, "ssp0 Rx"}, | ||||
| 
 | ||||
| 	{"AIF2 Playback", NULL, "ssp0 Tx"}, | ||||
| 	{"ssp0 Rx", NULL, "AIF2 Capture"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_stereo_spk_map[] = { | ||||
| 	{"Speaker", NULL, "SPOLP"}, | ||||
| 	{"Speaker", NULL, "SPOLN"}, | ||||
| 	{"Speaker", NULL, "SPORP"}, | ||||
| 	{"Speaker", NULL, "SPORN"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_soc_dapm_route byt_rt5640_mono_spk_map[] = { | ||||
| 	{"Speaker", NULL, "SPOLP"}, | ||||
| 	{"Speaker", NULL, "SPOLN"}, | ||||
| }; | ||||
| 
 | ||||
| static const struct snd_kcontrol_new byt_rt5640_controls[] = { | ||||
| 	SOC_DAPM_PIN_SWITCH("Headphone"), | ||||
| 	SOC_DAPM_PIN_SWITCH("Headset Mic"), | ||||
| @ -96,19 +263,46 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, | ||||
| 	struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	snd_soc_dai_set_bclk_ratio(codec_dai, 50); | ||||
| 
 | ||||
| 	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, | ||||
| 				     params_rate(params) * 512, | ||||
| 				     SND_SOC_CLOCK_IN); | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(rtd->dev, "can't set codec clock %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, | ||||
| 				  params_rate(params) * 50, | ||||
| 				  params_rate(params) * 512); | ||||
| 	if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) { | ||||
| 		/* use bitclock as PLL input */ | ||||
| 		if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || | ||||
| 			(byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { | ||||
| 
 | ||||
| 			/* 2x16 bit slots on SSP0 */ | ||||
| 			ret = snd_soc_dai_set_pll(codec_dai, 0, | ||||
| 						RT5640_PLL1_S_BCLK1, | ||||
| 						params_rate(params) * 32, | ||||
| 						params_rate(params) * 512); | ||||
| 		} else { | ||||
| 			/* 2x15 bit slots on SSP2 */ | ||||
| 			ret = snd_soc_dai_set_pll(codec_dai, 0, | ||||
| 						RT5640_PLL1_S_BCLK1, | ||||
| 						params_rate(params) * 50, | ||||
| 						params_rate(params) * 512); | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) { | ||||
| 			ret = snd_soc_dai_set_pll(codec_dai, 0, | ||||
| 						RT5640_PLL1_S_MCLK, | ||||
| 						25000000, | ||||
| 						params_rate(params) * 512); | ||||
| 		} else { | ||||
| 			ret = snd_soc_dai_set_pll(codec_dai, 0, | ||||
| 						RT5640_PLL1_S_MCLK, | ||||
| 						19200000, | ||||
| 						params_rate(params) * 512); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(rtd->dev, "can't set codec pll: %d\n", ret); | ||||
| 		return ret; | ||||
| @ -127,27 +321,73 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||||
| 			DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), | ||||
| 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||||
| 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | | ||||
| 						 BYT_RT5640_MCLK_EN), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), | ||||
| 			DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), | ||||
| 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||||
| 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | | ||||
| 						 BYT_RT5640_MONO_SPEAKER | | ||||
| 						 BYT_RT5640_DIFF_MIC | | ||||
| 						 BYT_RT5640_SSP0_AIF2 | | ||||
| 						 BYT_RT5640_MCLK_EN | ||||
| 						 ), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."), | ||||
| 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | | ||||
| 						 BYT_RT5640_DMIC_EN | | ||||
| 						 BYT_RT5640_MCLK_EN), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||||
| 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | | ||||
| 						 BYT_RT5640_MCLK_EN), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), | ||||
| 			DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP | | ||||
| 						 BYT_RT5640_DMIC_EN), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||||
| 			DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), | ||||
| 			DMI_MATCH(DMI_BOARD_NAME, "tPAD"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | | ||||
| 						BYT_RT5640_MCLK_EN | | ||||
| 						BYT_RT5640_SSP0_AIF1), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.callback = byt_rt5640_quirk_cb, | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_SYS_VENDOR, "Acer"), | ||||
| 			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), | ||||
| 		}, | ||||
| 		.driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | | ||||
| 						 BYT_RT5640_MCLK_EN | | ||||
| 						 BYT_RT5640_SSP0_AIF1), | ||||
| 
 | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| @ -158,13 +398,18 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) | ||||
| 	struct snd_soc_codec *codec = runtime->codec; | ||||
| 	struct snd_soc_card *card = runtime->card; | ||||
| 	const struct snd_soc_dapm_route *custom_map; | ||||
| 	struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); | ||||
| 	int num_routes; | ||||
| 
 | ||||
| 	card->dapm.idle_bias_off = true; | ||||
| 
 | ||||
| 	rt5640_sel_asrc_clk_src(codec, | ||||
| 				RT5640_DA_STEREO_FILTER | | ||||
| 				RT5640_AD_STEREO_FILTER, | ||||
| 				RT5640_DA_MONO_L_FILTER	| | ||||
| 				RT5640_DA_MONO_R_FILTER	| | ||||
| 				RT5640_AD_STEREO_FILTER	| | ||||
| 				RT5640_AD_MONO_L_FILTER	| | ||||
| 				RT5640_AD_MONO_R_FILTER, | ||||
| 				RT5640_CLK_SEL_ASRC); | ||||
| 
 | ||||
| 	ret = snd_soc_add_card_controls(card, byt_rt5640_controls, | ||||
| @ -179,6 +424,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) | ||||
| 		custom_map = byt_rt5640_intmic_in1_map; | ||||
| 		num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); | ||||
| 		break; | ||||
| 	case BYT_RT5640_IN3_MAP: | ||||
| 		custom_map = byt_rt5640_intmic_in3_map; | ||||
| 		num_routes = ARRAY_SIZE(byt_rt5640_intmic_in3_map); | ||||
| 		break; | ||||
| 	case BYT_RT5640_DMIC2_MAP: | ||||
| 		custom_map = byt_rt5640_intmic_dmic2_map; | ||||
| 		num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); | ||||
| @ -192,6 +441,43 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) { | ||||
| 		ret = snd_soc_dapm_add_routes(&card->dapm, | ||||
| 					byt_rt5640_ssp2_aif2_map, | ||||
| 					ARRAY_SIZE(byt_rt5640_ssp2_aif2_map)); | ||||
| 	} else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) { | ||||
| 		ret = snd_soc_dapm_add_routes(&card->dapm, | ||||
| 					byt_rt5640_ssp0_aif1_map, | ||||
| 					ARRAY_SIZE(byt_rt5640_ssp0_aif1_map)); | ||||
| 	} else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) { | ||||
| 		ret = snd_soc_dapm_add_routes(&card->dapm, | ||||
| 					byt_rt5640_ssp0_aif2_map, | ||||
| 					ARRAY_SIZE(byt_rt5640_ssp0_aif2_map)); | ||||
| 	} else { | ||||
| 		ret = snd_soc_dapm_add_routes(&card->dapm, | ||||
| 					byt_rt5640_ssp2_aif1_map, | ||||
| 					ARRAY_SIZE(byt_rt5640_ssp2_aif1_map)); | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) { | ||||
| 		ret = snd_soc_dapm_add_routes(&card->dapm, | ||||
| 					byt_rt5640_mono_spk_map, | ||||
| 					ARRAY_SIZE(byt_rt5640_mono_spk_map)); | ||||
| 	} else { | ||||
| 		ret = snd_soc_dapm_add_routes(&card->dapm, | ||||
| 					byt_rt5640_stereo_spk_map, | ||||
| 					ARRAY_SIZE(byt_rt5640_stereo_spk_map)); | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) { | ||||
| 		snd_soc_update_bits(codec,  RT5640_IN1_IN2, RT5640_IN_DF1, | ||||
| 				    RT5640_IN_DF1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { | ||||
| 		ret = rt5640_dmic_enable(codec, 0, 0); | ||||
| 		if (ret) | ||||
| @ -201,6 +487,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) | ||||
| 	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); | ||||
| 	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); | ||||
| 
 | ||||
| 	if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { | ||||
| 		/*
 | ||||
| 		 * The firmware might enable the clock at | ||||
| 		 * boot (this information may or may not | ||||
| 		 * be reflected in the enable clock register). | ||||
| 		 * To change the rate we must disable the clock | ||||
| 		 * first to cover these cases. Due to common | ||||
| 		 * clock framework restrictions that do not allow | ||||
| 		 * to disable a clock that has not been enabled, | ||||
| 		 * we need to enable the clock first. | ||||
| 		 */ | ||||
| 		ret = clk_prepare_enable(priv->mclk); | ||||
| 		if (!ret) | ||||
| 			clk_disable_unprepare(priv->mclk); | ||||
| 
 | ||||
| 		if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) | ||||
| 			ret = clk_set_rate(priv->mclk, 25000000); | ||||
| 		else | ||||
| 			ret = clk_set_rate(priv->mclk, 19200000); | ||||
| 
 | ||||
| 		if (ret) | ||||
| 			dev_err(card->dev, "unable to set MCLK rate\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| @ -221,34 +531,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, | ||||
| 						SNDRV_PCM_HW_PARAM_CHANNELS); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* The DSP will covert the FE rate to 48k, stereo, 24bits */ | ||||
| 	/* The DSP will covert the FE rate to 48k, stereo */ | ||||
| 	rate->min = rate->max = 48000; | ||||
| 	channels->min = channels->max = 2; | ||||
| 
 | ||||
| 	/* set SSP2 to 24-bit */ | ||||
| 	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); | ||||
| 	if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || | ||||
| 		(byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Default mode for SSP configuration is TDM 4 slot, override config | ||||
| 	 * with explicit setting to I2S 2ch 24-bit. The word length is set with | ||||
| 	 * dai_set_tdm_slot() since there is no other API exposed | ||||
| 	 */ | ||||
| 	ret = snd_soc_dai_set_fmt(rtd->cpu_dai, | ||||
| 				  SND_SOC_DAIFMT_I2S     | | ||||
| 				  SND_SOC_DAIFMT_NB_IF   | | ||||
| 				  SND_SOC_DAIFMT_CBS_CFS | ||||
| 				  ); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | ||||
| 		return ret; | ||||
| 		/* set SSP0 to 16-bit */ | ||||
| 		params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Default mode for SSP configuration is TDM 4 slot, override config | ||||
| 		 * with explicit setting to I2S 2ch 16-bit. The word length is set with | ||||
| 		 * dai_set_tdm_slot() since there is no other API exposed | ||||
| 		 */ | ||||
| 		ret = snd_soc_dai_set_fmt(rtd->cpu_dai, | ||||
| 					SND_SOC_DAIFMT_I2S     | | ||||
| 					SND_SOC_DAIFMT_NB_IF   | | ||||
| 					SND_SOC_DAIFMT_CBS_CFS | ||||
| 			); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 	} else { | ||||
| 
 | ||||
| 		/* set SSP2 to 24-bit */ | ||||
| 		params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Default mode for SSP configuration is TDM 4 slot, override config | ||||
| 		 * with explicit setting to I2S 2ch 24-bit. The word length is set with | ||||
| 		 * dai_set_tdm_slot() since there is no other API exposed | ||||
| 		 */ | ||||
| 		ret = snd_soc_dai_set_fmt(rtd->cpu_dai, | ||||
| 					SND_SOC_DAIFMT_I2S     | | ||||
| 					SND_SOC_DAIFMT_NB_IF   | | ||||
| 					SND_SOC_DAIFMT_CBS_CFS | ||||
| 			); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -305,10 +644,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { | ||||
| 	{ | ||||
| 		.name = "SSP2-Codec", | ||||
| 		.id = 1, | ||||
| 		.cpu_dai_name = "ssp2-port", | ||||
| 		.cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */ | ||||
| 		.platform_name = "sst-mfld-platform", | ||||
| 		.no_pcm = 1, | ||||
| 		.codec_dai_name = "rt5640-aif1", | ||||
| 		.codec_dai_name = "rt5640-aif1", /* changed w/ quirk */ | ||||
| 		.codec_name = "i2c-10EC5640:00", /* overwritten with HID */ | ||||
| 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||||
| 						| SND_SOC_DAIFMT_CBS_CFS, | ||||
| @ -335,6 +674,21 @@ static struct snd_soc_card byt_rt5640_card = { | ||||
| }; | ||||
| 
 | ||||
| static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ | ||||
| static char byt_rt5640_codec_aif_name[12]; /*  = "rt5640-aif[1|2]" */ | ||||
| static char byt_rt5640_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */ | ||||
| 
 | ||||
| static bool is_valleyview(void) | ||||
| { | ||||
| 	static const struct x86_cpu_id cpu_ids[] = { | ||||
| 		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ | ||||
| 		{} | ||||
| 	}; | ||||
| 
 | ||||
| 	if (!x86_match_cpu(cpu_ids)) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) | ||||
| { | ||||
| @ -343,10 +697,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) | ||||
| 	const char *i2c_name = NULL; | ||||
| 	int i; | ||||
| 	int dai_index; | ||||
| 	struct byt_rt5640_private *priv; | ||||
| 
 | ||||
| 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); | ||||
| 	if (!priv) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* register the soc card */ | ||||
| 	byt_rt5640_card.dev = &pdev->dev; | ||||
| 	mach = byt_rt5640_card.dev->platform_data; | ||||
| 	snd_soc_card_set_drvdata(&byt_rt5640_card, priv); | ||||
| 
 | ||||
| 	/* fix index of codec dai */ | ||||
| 	dai_index = MERR_DPCM_COMPR + 1; | ||||
| @ -366,8 +726,57 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) | ||||
| 		byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * swap SSP0 if bytcr is detected | ||||
| 	 * (will be overridden if DMI quirk is detected) | ||||
| 	 */ | ||||
| 	if (is_valleyview()) { | ||||
| 		struct sst_platform_info *p_info = mach->pdata; | ||||
| 		const struct sst_res_info *res_info = p_info->res_info; | ||||
| 
 | ||||
| 		/* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */ | ||||
| 		if (res_info->acpi_ipc_irq_index == 0) { | ||||
| 			byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* check quirks before creating card */ | ||||
| 	dmi_check_system(byt_rt5640_quirk_table); | ||||
| 	log_quirks(&pdev->dev); | ||||
| 
 | ||||
| 	if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) || | ||||
| 	    (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { | ||||
| 
 | ||||
| 		/* fixup codec aif name */ | ||||
| 		snprintf(byt_rt5640_codec_aif_name, | ||||
| 			sizeof(byt_rt5640_codec_aif_name), | ||||
| 			"%s", "rt5640-aif2"); | ||||
| 
 | ||||
| 		byt_rt5640_dais[dai_index].codec_dai_name = | ||||
| 			byt_rt5640_codec_aif_name; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || | ||||
| 	    (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { | ||||
| 
 | ||||
| 		/* fixup cpu dai name name */ | ||||
| 		snprintf(byt_rt5640_cpu_dai_name, | ||||
| 			sizeof(byt_rt5640_cpu_dai_name), | ||||
| 			"%s", "ssp0-port"); | ||||
| 
 | ||||
| 		byt_rt5640_dais[dai_index].cpu_dai_name = | ||||
| 			byt_rt5640_cpu_dai_name; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) { | ||||
| 		priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); | ||||
| 		if (IS_ERR(priv->mclk)) { | ||||
| 			dev_err(&pdev->dev, | ||||
| 				"Failed to get MCLK from pmc_plt_clk_3: %ld\n", | ||||
| 				PTR_ERR(priv->mclk)); | ||||
| 			return PTR_ERR(priv->mclk); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); | ||||
| 
 | ||||
|  | ||||
| @ -199,6 +199,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { | ||||
| 
 | ||||
| static struct sst_acpi_mach broadwell_machines[] = { | ||||
| 	{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, | ||||
| 	{ "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL }, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
| #include "../common/sst-dsp.h" | ||||
| #include "../common/sst-dsp-priv.h" | ||||
| #include "skl-sst-ipc.h" | ||||
| #include "skl-tplg-interface.h" | ||||
| 
 | ||||
| #define BXT_BASEFW_TIMEOUT	3000 | ||||
| #define BXT_INIT_TIMEOUT	500 | ||||
| @ -40,11 +41,73 @@ | ||||
| #define BXT_INSTANCE_ID 0 | ||||
| #define BXT_BASE_FW_MODULE_ID 0 | ||||
| 
 | ||||
| #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 | ||||
| 
 | ||||
| static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) | ||||
| { | ||||
| 	 return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo) | ||||
| { | ||||
| 	struct snd_dma_buffer dmab; | ||||
| 	struct skl_sst *skl = ctx->thread_context; | ||||
| 	const struct firmware *fw = NULL; | ||||
| 	struct firmware stripped_fw; | ||||
| 	int ret = 0, i, dma_id, stream_tag; | ||||
| 
 | ||||
| 	/* library indices start from 1 to N. 0 represents base FW */ | ||||
| 	for (i = 1; i < minfo->lib_count; i++) { | ||||
| 		ret = request_firmware(&fw, minfo->lib[i].name, ctx->dev); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(ctx->dev, "Request lib %s failed:%d\n", | ||||
| 					minfo->lib[i].name, ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		if (skl->is_first_boot) { | ||||
| 			ret = snd_skl_parse_uuids(ctx, fw, | ||||
| 					BXT_ADSP_FW_BIN_HDR_OFFSET, i); | ||||
| 			if (ret < 0) | ||||
| 				goto load_library_failed; | ||||
| 		} | ||||
| 
 | ||||
| 		stripped_fw.data = fw->data; | ||||
| 		stripped_fw.size = fw->size; | ||||
| 		skl_dsp_strip_extended_manifest(&stripped_fw); | ||||
| 
 | ||||
| 		stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, | ||||
| 					stripped_fw.size, &dmab); | ||||
| 		if (stream_tag <= 0) { | ||||
| 			dev_err(ctx->dev, "Lib prepare DMA err: %x\n", | ||||
| 					stream_tag); | ||||
| 			ret = stream_tag; | ||||
| 			goto load_library_failed; | ||||
| 		} | ||||
| 
 | ||||
| 		dma_id = stream_tag - 1; | ||||
| 		memcpy(dmab.area, stripped_fw.data, stripped_fw.size); | ||||
| 
 | ||||
| 		ctx->dsp_ops.trigger(ctx->dev, true, stream_tag); | ||||
| 		ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i); | ||||
| 		if (ret < 0) | ||||
| 			dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n", | ||||
| 					minfo->lib[i].name, ret); | ||||
| 
 | ||||
| 		ctx->dsp_ops.trigger(ctx->dev, false, stream_tag); | ||||
| 		ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag); | ||||
| 		release_firmware(fw); | ||||
| 		fw = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| 
 | ||||
| load_library_failed: | ||||
| 	release_firmware(fw); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * First boot sequence has some extra steps. Core 0 waits for power | ||||
|  * status on core 1, so power up core 1 also momentarily, keep it in | ||||
| @ -157,8 +220,6 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 | ||||
| 
 | ||||
| static int bxt_load_base_firmware(struct sst_dsp *ctx) | ||||
| { | ||||
| 	struct firmware stripped_fw; | ||||
| @ -175,9 +236,12 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) | ||||
| 	if (ctx->fw == NULL) | ||||
| 		goto sst_load_base_firmware_failed; | ||||
| 
 | ||||
| 	ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET); | ||||
| 	if (ret < 0) | ||||
| 		goto sst_load_base_firmware_failed; | ||||
| 	/* prase uuids on first boot */ | ||||
| 	if (skl->is_first_boot) { | ||||
| 		ret = snd_skl_parse_uuids(ctx, ctx->fw, BXT_ADSP_FW_BIN_HDR_OFFSET, 0); | ||||
| 		if (ret < 0) | ||||
| 			goto sst_load_base_firmware_failed; | ||||
| 	} | ||||
| 
 | ||||
| 	stripped_fw.data = ctx->fw->data; | ||||
| 	stripped_fw.size = ctx->fw->size; | ||||
| @ -230,12 +294,23 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) | ||||
| 	int ret; | ||||
| 	struct skl_ipc_dxstate_info dx; | ||||
| 	unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); | ||||
| 	struct skl_dfw_manifest *minfo = &skl->manifest; | ||||
| 
 | ||||
| 	if (skl->fw_loaded == false) { | ||||
| 		skl->boot_complete = false; | ||||
| 		ret = bxt_load_base_firmware(ctx); | ||||
| 		if (ret < 0) | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(ctx->dev, "reload fw failed: %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		if (minfo->lib_count > 1) { | ||||
| 			ret = bxt_load_library(ctx, minfo); | ||||
| 			if (ret < 0) { | ||||
| 				dev_err(ctx->dev, "reload libs failed: %d\n", ret); | ||||
| 				return ret; | ||||
| 			} | ||||
| 		} | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| @ -329,7 +404,7 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) | ||||
| 
 | ||||
| 	ret = skl_dsp_disable_core(ctx, core_mask); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ctx->dev, "Failed to disable core %d", ret); | ||||
| 		dev_err(ctx->dev, "Failed to disable core %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	skl->cores.state[core_id] = SKL_DSP_RESET; | ||||
| @ -341,6 +416,7 @@ static struct skl_dsp_fw_ops bxt_fw_ops = { | ||||
| 	.set_state_D3 = bxt_set_dsp_D3, | ||||
| 	.load_fw = bxt_load_base_firmware, | ||||
| 	.get_fw_errcode = bxt_get_errorcode, | ||||
| 	.load_library = bxt_load_library, | ||||
| }; | ||||
| 
 | ||||
| static struct sst_ops skl_ops = { | ||||
| @ -397,14 +473,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | ||||
| 	skl->cores.count = 2; | ||||
| 	skl->boot_complete = false; | ||||
| 	init_waitqueue_head(&skl->boot_wait); | ||||
| 
 | ||||
| 	ret = sst->fw_ops.load_fw(sst); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(dev, "Load base fw failed: %x", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	skl_dsp_init_core_state(sst); | ||||
| 	skl->is_first_boot = true; | ||||
| 
 | ||||
| 	if (dsp) | ||||
| 		*dsp = skl; | ||||
| @ -413,6 +482,31 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); | ||||
| 
 | ||||
| int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct sst_dsp *sst = ctx->dsp; | ||||
| 
 | ||||
| 	ret = sst->fw_ops.load_fw(sst); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(dev, "Load base fw failed: %x\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	skl_dsp_init_core_state(sst); | ||||
| 
 | ||||
| 	if (ctx->manifest.lib_count > 1) { | ||||
| 		ret = sst->fw_ops.load_library(sst, &ctx->manifest); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(dev, "Load Library failed : %x\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 	ctx->is_first_boot = false; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(bxt_sst_init_fw); | ||||
| 
 | ||||
| void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) | ||||
| { | ||||
|  | ||||
| @ -203,32 +203,35 @@ static const struct skl_dsp_ops dsp_ops[] = { | ||||
| 		.id = 0x9d70, | ||||
| 		.loader_ops = skl_get_loader_ops, | ||||
| 		.init = skl_sst_dsp_init, | ||||
| 		.init_fw = skl_sst_init_fw, | ||||
| 		.cleanup = skl_sst_dsp_cleanup | ||||
| 	}, | ||||
| 	{ | ||||
| 		.id = 0x9d71, | ||||
| 		.loader_ops = skl_get_loader_ops, | ||||
| 		.init = skl_sst_dsp_init, | ||||
| 		.init_fw = skl_sst_init_fw, | ||||
| 		.cleanup = skl_sst_dsp_cleanup | ||||
| 	}, | ||||
| 	{ | ||||
| 		.id = 0x5a98, | ||||
| 		.loader_ops = bxt_get_loader_ops, | ||||
| 		.init = bxt_sst_dsp_init, | ||||
| 		.init_fw = bxt_sst_init_fw, | ||||
| 		.cleanup = bxt_sst_dsp_cleanup | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int skl_get_dsp_ops(int pci_id) | ||||
| const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) { | ||||
| 		if (dsp_ops[i].id == pci_id) | ||||
| 			return i; | ||||
| 			return &dsp_ops[i]; | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| int skl_init_dsp(struct skl *skl) | ||||
| @ -238,7 +241,8 @@ int skl_init_dsp(struct skl *skl) | ||||
| 	struct hdac_bus *bus = ebus_to_hbus(ebus); | ||||
| 	struct skl_dsp_loader_ops loader_ops; | ||||
| 	int irq = bus->irq; | ||||
| 	int ret, index; | ||||
| 	const struct skl_dsp_ops *ops; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* enable ppcap interrupt */ | ||||
| 	snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); | ||||
| @ -251,18 +255,18 @@ int skl_init_dsp(struct skl *skl) | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	index  = skl_get_dsp_ops(skl->pci->device); | ||||
| 	if (index  < 0) | ||||
| 		return -EINVAL; | ||||
| 	ops = skl_get_dsp_ops(skl->pci->device); | ||||
| 	if (!ops) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	loader_ops = dsp_ops[index].loader_ops(); | ||||
| 	ret = dsp_ops[index].init(bus->dev, mmio_base, irq, | ||||
| 			skl->fw_name, loader_ops, &skl->skl_sst); | ||||
| 	loader_ops = ops->loader_ops(); | ||||
| 	ret = ops->init(bus->dev, mmio_base, irq, | ||||
| 				skl->fw_name, loader_ops, | ||||
| 				&skl->skl_sst); | ||||
| 
 | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	skl_dsp_enable_notification(skl->skl_sst, false); | ||||
| 	dev_dbg(bus->dev, "dsp registration status=%d\n", ret); | ||||
| 
 | ||||
| 	return ret; | ||||
| @ -273,16 +277,16 @@ int skl_free_dsp(struct skl *skl) | ||||
| 	struct hdac_ext_bus *ebus = &skl->ebus; | ||||
| 	struct hdac_bus *bus = ebus_to_hbus(ebus); | ||||
| 	struct skl_sst *ctx = skl->skl_sst; | ||||
| 	int index; | ||||
| 	const struct skl_dsp_ops *ops; | ||||
| 
 | ||||
| 	/* disable  ppcap interrupt */ | ||||
| 	snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); | ||||
| 
 | ||||
| 	index = skl_get_dsp_ops(skl->pci->device); | ||||
| 	if (index  < 0) | ||||
| 	ops = skl_get_dsp_ops(skl->pci->device); | ||||
| 	if (!ops) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	dsp_ops[index].cleanup(bus->dev, ctx); | ||||
| 	ops->cleanup(bus->dev, ctx); | ||||
| 
 | ||||
| 	if (ctx->dsp->addr.lpe) | ||||
| 		iounmap(ctx->dsp->addr.lpe); | ||||
| @ -296,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl) | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* if ppcap is not supported return 0 */ | ||||
| 	if (!skl->ebus.ppcap) | ||||
| 	if (!skl->ebus.bus.ppcap) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	ret = skl_dsp_sleep(ctx->dsp); | ||||
| @ -316,13 +320,17 @@ int skl_resume_dsp(struct skl *skl) | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* if ppcap is not supported return 0 */ | ||||
| 	if (!skl->ebus.ppcap) | ||||
| 	if (!skl->ebus.bus.ppcap) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* enable ppcap interrupt */ | ||||
| 	snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); | ||||
| 	snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); | ||||
| 
 | ||||
| 	/* check if DSP 1st boot is done */ | ||||
| 	if (skl->skl_sst->is_first_boot == true) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	ret = skl_dsp_wake(ctx->dsp); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| @ -672,6 +680,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, | ||||
| 		return param_size; | ||||
| 
 | ||||
| 	case SKL_MODULE_TYPE_BASE_OUTFMT: | ||||
| 	case SKL_MODULE_TYPE_KPB: | ||||
| 		return sizeof(struct skl_base_outfmt_cfg); | ||||
| 
 | ||||
| 	default: | ||||
| @ -725,6 +734,7 @@ static int skl_set_module_format(struct skl_sst *ctx, | ||||
| 		break; | ||||
| 
 | ||||
| 	case SKL_MODULE_TYPE_BASE_OUTFMT: | ||||
| 	case SKL_MODULE_TYPE_KPB: | ||||
| 		skl_set_base_outfmt_format(ctx, module_config, *param_data); | ||||
| 		break; | ||||
| 
 | ||||
| @ -779,6 +789,7 @@ static int skl_alloc_queue(struct skl_module_pin *mpin, | ||||
| 				mpin[i].in_use = true; | ||||
| 				mpin[i].id.module_id = id.module_id; | ||||
| 				mpin[i].id.instance_id = id.instance_id; | ||||
| 				mpin[i].id.pvt_id = id.pvt_id; | ||||
| 				mpin[i].tgt_mcfg = tgt_cfg; | ||||
| 				return i; | ||||
| 			} | ||||
| @ -802,6 +813,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index) | ||||
| 		mpin[q_index].in_use = false; | ||||
| 		mpin[q_index].id.module_id = 0; | ||||
| 		mpin[q_index].id.instance_id = 0; | ||||
| 		mpin[q_index].id.pvt_id = 0; | ||||
| 	} | ||||
| 	mpin[q_index].pin_state = SKL_PIN_UNBIND; | ||||
| 	mpin[q_index].tgt_mcfg = NULL; | ||||
| @ -842,7 +854,7 @@ int skl_init_module(struct skl_sst *ctx, | ||||
| 	struct skl_ipc_init_instance_msg msg; | ||||
| 
 | ||||
| 	dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, | ||||
| 		 mconfig->id.module_id, mconfig->id.instance_id); | ||||
| 		 mconfig->id.module_id, mconfig->id.pvt_id); | ||||
| 
 | ||||
| 	if (mconfig->pipe->state != SKL_PIPE_CREATED) { | ||||
| 		dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n", | ||||
| @ -858,10 +870,11 @@ int skl_init_module(struct skl_sst *ctx, | ||||
| 	} | ||||
| 
 | ||||
| 	msg.module_id = mconfig->id.module_id; | ||||
| 	msg.instance_id = mconfig->id.instance_id; | ||||
| 	msg.instance_id = mconfig->id.pvt_id; | ||||
| 	msg.ppl_instance_id = mconfig->pipe->ppl_id; | ||||
| 	msg.param_data_size = module_config_size; | ||||
| 	msg.core_id = mconfig->core_id; | ||||
| 	msg.domain = mconfig->domain; | ||||
| 
 | ||||
| 	ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data); | ||||
| 	if (ret < 0) { | ||||
| @ -878,9 +891,9 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg | ||||
| 	*src_module, struct skl_module_cfg *dst_module) | ||||
| { | ||||
| 	dev_dbg(ctx->dev, "%s: src module_id = %d  src_instance=%d\n", | ||||
| 		__func__, src_module->id.module_id, src_module->id.instance_id); | ||||
| 		__func__, src_module->id.module_id, src_module->id.pvt_id); | ||||
| 	dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__, | ||||
| 		 dst_module->id.module_id, dst_module->id.instance_id); | ||||
| 		 dst_module->id.module_id, dst_module->id.pvt_id); | ||||
| 
 | ||||
| 	dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n", | ||||
| 		src_module->m_state, dst_module->m_state); | ||||
| @ -927,9 +940,9 @@ int skl_unbind_modules(struct skl_sst *ctx, | ||||
| 		return 0; | ||||
| 
 | ||||
| 	msg.module_id = src_mcfg->id.module_id; | ||||
| 	msg.instance_id = src_mcfg->id.instance_id; | ||||
| 	msg.instance_id = src_mcfg->id.pvt_id; | ||||
| 	msg.dst_module_id = dst_mcfg->id.module_id; | ||||
| 	msg.dst_instance_id = dst_mcfg->id.instance_id; | ||||
| 	msg.dst_instance_id = dst_mcfg->id.pvt_id; | ||||
| 	msg.bind = false; | ||||
| 
 | ||||
| 	ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); | ||||
| @ -988,9 +1001,9 @@ int skl_bind_modules(struct skl_sst *ctx, | ||||
| 			 msg.src_queue, msg.dst_queue); | ||||
| 
 | ||||
| 	msg.module_id = src_mcfg->id.module_id; | ||||
| 	msg.instance_id = src_mcfg->id.instance_id; | ||||
| 	msg.instance_id = src_mcfg->id.pvt_id; | ||||
| 	msg.dst_module_id = dst_mcfg->id.module_id; | ||||
| 	msg.dst_instance_id = dst_mcfg->id.instance_id; | ||||
| 	msg.dst_instance_id = dst_mcfg->id.pvt_id; | ||||
| 	msg.bind = true; | ||||
| 
 | ||||
| 	ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); | ||||
| @ -1168,7 +1181,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, | ||||
| 	struct skl_ipc_large_config_msg msg; | ||||
| 
 | ||||
| 	msg.module_id = mcfg->id.module_id; | ||||
| 	msg.instance_id = mcfg->id.instance_id; | ||||
| 	msg.instance_id = mcfg->id.pvt_id; | ||||
| 	msg.param_data_size = size; | ||||
| 	msg.large_param_id = param_id; | ||||
| 
 | ||||
| @ -1181,7 +1194,7 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, | ||||
| 	struct skl_ipc_large_config_msg msg; | ||||
| 
 | ||||
| 	msg.module_id = mcfg->id.module_id; | ||||
| 	msg.instance_id = mcfg->id.instance_id; | ||||
| 	msg.instance_id = mcfg->id.pvt_id; | ||||
| 	msg.param_data_size = size; | ||||
| 	msg.large_param_id = param_id; | ||||
| 
 | ||||
|  | ||||
| @ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, | ||||
| 
 | ||||
| static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus) | ||||
| { | ||||
| 	if (ebus->ppcap) | ||||
| 	if ((ebus_to_hbus(ebus))->ppcap) | ||||
| 		return HDAC_EXT_STREAM_TYPE_HOST; | ||||
| 	else | ||||
| 		return HDAC_EXT_STREAM_TYPE_COUPLED; | ||||
| @ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream, | ||||
| 	struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); | ||||
| 	int format_val = 0; | ||||
| 
 | ||||
| 	if (ebus->ppcap) { | ||||
| 	if ((ebus_to_hbus(ebus))->ppcap) { | ||||
| 		struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 
 | ||||
| 		format_val = snd_hdac_calc_stream_format(runtime->rate, | ||||
| @ -648,7 +648,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { | ||||
| 		.channels_min = HDA_MONO, | ||||
| 		.channels_max = HDA_STEREO, | ||||
| 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, | ||||
| 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, | ||||
| 		.formats = SNDRV_PCM_FMTBIT_S16_LE | | ||||
| 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, | ||||
| 	}, | ||||
| 	.capture = { | ||||
| 		.stream_name = "System Capture", | ||||
| @ -1020,7 +1021,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, | ||||
| { | ||||
| 	struct hdac_ext_bus *ebus = get_bus_ctx(substream); | ||||
| 
 | ||||
| 	if (!ebus->ppcap) | ||||
| 	if (!(ebus_to_hbus(ebus))->ppcap) | ||||
| 		return skl_coupled_trigger(substream, cmd); | ||||
| 
 | ||||
| 	return 0; | ||||
| @ -1138,20 +1139,67 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) | ||||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| static int skl_populate_modules(struct skl *skl) | ||||
| { | ||||
| 	struct skl_pipeline *p; | ||||
| 	struct skl_pipe_module *m; | ||||
| 	struct snd_soc_dapm_widget *w; | ||||
| 	struct skl_module_cfg *mconfig; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	list_for_each_entry(p, &skl->ppl_list, node) { | ||||
| 		list_for_each_entry(m, &p->pipe->w_list, node) { | ||||
| 
 | ||||
| 			w = m->w; | ||||
| 			mconfig = w->priv; | ||||
| 
 | ||||
| 			ret = snd_skl_get_module_info(skl->skl_sst, mconfig); | ||||
| 			if (ret < 0) { | ||||
| 				dev_err(skl->skl_sst->dev, | ||||
| 					"query module info failed:%d\n", ret); | ||||
| 				goto err; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| err: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int skl_platform_soc_probe(struct snd_soc_platform *platform) | ||||
| { | ||||
| 	struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); | ||||
| 	struct skl *skl = ebus_to_skl(ebus); | ||||
| 	const struct skl_dsp_ops *ops; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (ebus->ppcap) { | ||||
| 	pm_runtime_get_sync(platform->dev); | ||||
| 	if ((ebus_to_hbus(ebus))->ppcap) { | ||||
| 		ret = skl_tplg_init(platform, ebus); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(platform->dev, "Failed to init topology!\n"); | ||||
| 			return ret; | ||||
| 		} | ||||
| 		skl->platform = platform; | ||||
| 
 | ||||
| 		/* load the firmwares, since all is set */ | ||||
| 		ops = skl_get_dsp_ops(skl->pci->device); | ||||
| 		if (!ops) | ||||
| 			return -EIO; | ||||
| 
 | ||||
| 		if (skl->skl_sst->is_first_boot == false) { | ||||
| 			dev_err(platform->dev, "DSP reports first boot done!!!\n"); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = ops->init_fw(platform->dev, skl->skl_sst); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(platform->dev, "Failed to boot first fw: %d\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 		skl_populate_modules(skl); | ||||
| 	} | ||||
| 	pm_runtime_mark_last_busy(platform->dev); | ||||
| 	pm_runtime_put_autosuspend(platform->dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @ -341,14 +341,14 @@ int skl_cldma_prepare(struct sst_dsp *ctx) | ||||
| 	ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, | ||||
| 			&ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret); | ||||
| 		dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	/* Setup Code loader BDL */ | ||||
| 	ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, | ||||
| 			&ctx->cl_dev.dmab_bdl, PAGE_SIZE); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret); | ||||
| 		dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret); | ||||
| 		ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
| #include <sound/memalloc.h> | ||||
| #include "skl-sst-cldma.h" | ||||
| #include "skl-tplg-interface.h" | ||||
| #include "skl-topology.h" | ||||
| 
 | ||||
| struct sst_dsp; | ||||
| struct skl_sst; | ||||
| @ -133,6 +134,8 @@ enum skl_dsp_states { | ||||
| struct skl_dsp_fw_ops { | ||||
| 	int (*load_fw)(struct sst_dsp  *ctx); | ||||
| 	/* FW module parser/loader */ | ||||
| 	int (*load_library)(struct sst_dsp *ctx, | ||||
| 		struct skl_dfw_manifest *minfo); | ||||
| 	int (*parse_fw)(struct sst_dsp *ctx); | ||||
| 	int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); | ||||
| 	int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); | ||||
| @ -203,12 +206,21 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | ||||
| int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | ||||
| 		const char *fw_name, struct skl_dsp_loader_ops dsp_ops, | ||||
| 		struct skl_sst **dsp); | ||||
| int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); | ||||
| int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); | ||||
| void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); | ||||
| void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); | ||||
| 
 | ||||
| int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, | ||||
| 		struct skl_dfw_module *dfw_config); | ||||
| int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset); | ||||
| int snd_skl_get_module_info(struct skl_sst *ctx, | ||||
| 				struct skl_module_cfg *mconfig); | ||||
| int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, | ||||
| 				unsigned int offset, int index); | ||||
| int skl_get_pvt_id(struct skl_sst *ctx, | ||||
| 				struct skl_module_cfg *mconfig); | ||||
| int skl_put_pvt_id(struct skl_sst *ctx, | ||||
| 				struct skl_module_cfg *mconfig); | ||||
| int skl_get_pvt_instance_id_map(struct skl_sst *ctx, | ||||
| 				int module_id, int instance_id); | ||||
| void skl_freeup_uuid_list(struct skl_sst *ctx); | ||||
| 
 | ||||
| int skl_dsp_strip_extended_manifest(struct firmware *fw); | ||||
|  | ||||
| @ -114,6 +114,11 @@ | ||||
| #define IPC_CORE_ID(x)			(((x) & IPC_CORE_ID_MASK) \ | ||||
| 					<< IPC_CORE_ID_SHIFT) | ||||
| 
 | ||||
| #define IPC_DOMAIN_SHIFT                28 | ||||
| #define IPC_DOMAIN_MASK                 0x1 | ||||
| #define IPC_DOMAIN(x)                   (((x) & IPC_DOMAIN_MASK) \ | ||||
| 					<< IPC_DOMAIN_SHIFT) | ||||
| 
 | ||||
| /* Bind/Unbind message extension register */ | ||||
| #define IPC_DST_MOD_ID_SHIFT		0 | ||||
| #define IPC_DST_MOD_ID(x)		(((x) & IPC_MOD_ID_MASK) \ | ||||
| @ -190,6 +195,7 @@ enum skl_ipc_glb_type { | ||||
| 	IPC_GLB_GET_PPL_CONTEXT_SIZE = 21, | ||||
| 	IPC_GLB_SAVE_PPL = 22, | ||||
| 	IPC_GLB_RESTORE_PPL = 23, | ||||
| 	IPC_GLB_LOAD_LIBRARY = 24, | ||||
| 	IPC_GLB_NOTIFY = 26, | ||||
| 	IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ | ||||
| }; | ||||
| @ -338,7 +344,7 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc, | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			dev_err(ipc->dev, "ipc: Unhandled error msg=%x", | ||||
| 			dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", | ||||
| 						header.primary); | ||||
| 			break; | ||||
| 		} | ||||
| @ -379,13 +385,13 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply); | ||||
| 		dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", reply); | ||||
| 		msg->errno = -EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (reply != IPC_GLB_REPLY_SUCCESS) { | ||||
| 		dev_err(ipc->dev, "ipc FW reply: reply=%d", reply); | ||||
| 		dev_err(ipc->dev, "ipc FW reply: reply=%d\n", reply); | ||||
| 		dev_err(ipc->dev, "FW Error Code: %u\n", | ||||
| 			ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); | ||||
| 	} | ||||
| @ -434,9 +440,9 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) | ||||
| 		hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE); | ||||
| 		header.primary = hipct; | ||||
| 		header.extension = hipcte; | ||||
| 		dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x", | ||||
| 		dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x\n", | ||||
| 						header.primary); | ||||
| 		dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x", | ||||
| 		dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x\n", | ||||
| 						header.extension); | ||||
| 
 | ||||
| 		if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) { | ||||
| @ -704,6 +710,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc, | ||||
| 	header.extension = IPC_CORE_ID(msg->core_id); | ||||
| 	header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id); | ||||
| 	header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size); | ||||
| 	header.extension |= IPC_DOMAIN(msg->domain); | ||||
| 
 | ||||
| 	dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, | ||||
| 			 header.primary, header.extension); | ||||
| @ -742,7 +749,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, | ||||
| 			 header.extension); | ||||
| 	ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ipc->dev, "ipc: bind/unbind faileden"); | ||||
| 		dev_err(ipc->dev, "ipc: bind/unbind failed\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| @ -902,3 +909,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); | ||||
| 
 | ||||
| int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, | ||||
| 				u8 dma_id, u8 table_id) | ||||
| { | ||||
| 	struct skl_ipc_header header = {0}; | ||||
| 	u64 *ipc_header = (u64 *)(&header); | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | ||||
| 	header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | ||||
| 	header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_LIBRARY); | ||||
| 	header.primary |= IPC_MOD_INSTANCE_ID(table_id); | ||||
| 	header.primary |= IPC_MOD_ID(dma_id); | ||||
| 
 | ||||
| 	ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); | ||||
| 
 | ||||
| 	if (ret < 0) | ||||
| 		dev_err(ipc->dev, "ipc: load lib failed\n"); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library); | ||||
|  | ||||
| @ -66,7 +66,7 @@ struct skl_sst { | ||||
| 
 | ||||
| 	/* callback for miscbdge */ | ||||
| 	void (*enable_miscbdcge)(struct device *dev, bool enable); | ||||
| 	/*Is CGCTL.MISCBDCGE disabled*/ | ||||
| 	/* Is CGCTL.MISCBDCGE disabled */ | ||||
| 	bool miscbdcg_disabled; | ||||
| 
 | ||||
| 	/* Populate module information */ | ||||
| @ -75,8 +75,14 @@ struct skl_sst { | ||||
| 	/* Is firmware loaded */ | ||||
| 	bool fw_loaded; | ||||
| 
 | ||||
| 	/* first boot ? */ | ||||
| 	bool is_first_boot; | ||||
| 
 | ||||
| 	/* multi-core */ | ||||
| 	struct skl_dsp_cores cores; | ||||
| 
 | ||||
| 	/* tplg manifest */ | ||||
| 	struct skl_dfw_manifest manifest; | ||||
| }; | ||||
| 
 | ||||
| struct skl_ipc_init_instance_msg { | ||||
| @ -85,6 +91,7 @@ struct skl_ipc_init_instance_msg { | ||||
| 	u16 param_data_size; | ||||
| 	u8 ppl_instance_id; | ||||
| 	u8 core_id; | ||||
| 	u8 domain; | ||||
| }; | ||||
| 
 | ||||
| struct skl_ipc_bind_unbind_msg { | ||||
| @ -145,6 +152,9 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, | ||||
| int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, | ||||
| 		struct skl_ipc_large_config_msg *msg, u32 *param); | ||||
| 
 | ||||
| int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, | ||||
| 			u8 dma_id, u8 table_id); | ||||
| 
 | ||||
| void skl_ipc_int_enable(struct sst_dsp *dsp); | ||||
| void skl_ipc_op_int_enable(struct sst_dsp *ctx); | ||||
| void skl_ipc_op_int_disable(struct sst_dsp *ctx); | ||||
|  | ||||
| @ -28,11 +28,6 @@ | ||||
| /* FW Extended Manifest Header id = $AE1 */ | ||||
| #define SKL_EXT_MANIFEST_HEADER_MAGIC   0x31454124 | ||||
| 
 | ||||
| struct skl_dfw_module_mod { | ||||
| 	char name[100]; | ||||
| 	struct skl_dfw_module skl_dfw_mod; | ||||
| }; | ||||
| 
 | ||||
| struct UUID { | ||||
| 	u8 id[16]; | ||||
| }; | ||||
| @ -99,10 +94,15 @@ struct adsp_fw_hdr { | ||||
| 	u32 load_offset; | ||||
| } __packed; | ||||
| 
 | ||||
| #define MAX_INSTANCE_BUFF 2 | ||||
| 
 | ||||
| struct uuid_module { | ||||
| 	uuid_le uuid; | ||||
| 	int id; | ||||
| 	int is_loadable; | ||||
| 	int max_instance; | ||||
| 	u64 pvt_id[MAX_INSTANCE_BUFF]; | ||||
| 	int *instance_id; | ||||
| 
 | ||||
| 	struct list_head list; | ||||
| }; | ||||
| @ -115,13 +115,13 @@ struct skl_ext_manifest_hdr { | ||||
| 	u32 entries; | ||||
| }; | ||||
| 
 | ||||
| int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, | ||||
| 			struct skl_dfw_module *dfw_config) | ||||
| int snd_skl_get_module_info(struct skl_sst *ctx, | ||||
| 				struct skl_module_cfg *mconfig) | ||||
| { | ||||
| 	struct uuid_module *module; | ||||
| 	uuid_le *uuid_mod; | ||||
| 
 | ||||
| 	uuid_mod = (uuid_le *)uuid; | ||||
| 	uuid_mod = (uuid_le *)mconfig->guid; | ||||
| 
 | ||||
| 	if (list_empty(&ctx->uuid_list)) { | ||||
| 		dev_err(ctx->dev, "Module list is empty\n"); | ||||
| @ -130,8 +130,8 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, | ||||
| 
 | ||||
| 	list_for_each_entry(module, &ctx->uuid_list, list) { | ||||
| 		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { | ||||
| 			dfw_config->module_id = module->id; | ||||
| 			dfw_config->is_loadable = module->is_loadable; | ||||
| 			mconfig->id.module_id = module->id; | ||||
| 			mconfig->is_loadable = module->is_loadable; | ||||
| 
 | ||||
| 			return 0; | ||||
| 		} | ||||
| @ -141,15 +141,154 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_skl_get_module_info); | ||||
| 
 | ||||
| static int skl_get_pvtid_map(struct uuid_module *module, int instance_id) | ||||
| { | ||||
| 	int pvt_id; | ||||
| 
 | ||||
| 	for (pvt_id = 0; pvt_id < module->max_instance; pvt_id++) { | ||||
| 		if (module->instance_id[pvt_id] == instance_id) | ||||
| 			return pvt_id; | ||||
| 	} | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| int skl_get_pvt_instance_id_map(struct skl_sst *ctx, | ||||
| 				int module_id, int instance_id) | ||||
| { | ||||
| 	struct uuid_module *module; | ||||
| 
 | ||||
| 	list_for_each_entry(module, &ctx->uuid_list, list) { | ||||
| 		if (module->id == module_id) | ||||
| 			return skl_get_pvtid_map(module, instance_id); | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_get_pvt_instance_id_map); | ||||
| 
 | ||||
| static inline int skl_getid_32(struct uuid_module *module, u64 *val, | ||||
| 				int word1_mask, int word2_mask) | ||||
| { | ||||
| 	int index, max_inst, pvt_id; | ||||
| 	u32 mask_val; | ||||
| 
 | ||||
| 	max_inst =  module->max_instance; | ||||
| 	mask_val = (u32)(*val >> word1_mask); | ||||
| 
 | ||||
| 	if (mask_val != 0xffffffff) { | ||||
| 		index = ffz(mask_val); | ||||
| 		pvt_id = index + word1_mask + word2_mask; | ||||
| 		if (pvt_id <= (max_inst - 1)) { | ||||
| 			*val |= 1 << (index + word1_mask); | ||||
| 			return pvt_id; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static inline int skl_pvtid_128(struct uuid_module *module) | ||||
| { | ||||
| 	int j, i, word1_mask, word2_mask = 0, pvt_id; | ||||
| 
 | ||||
| 	for (j = 0; j < MAX_INSTANCE_BUFF; j++) { | ||||
| 		word1_mask = 0; | ||||
| 
 | ||||
| 		for (i = 0; i < 2; i++) { | ||||
| 			pvt_id = skl_getid_32(module, &module->pvt_id[j], | ||||
| 						word1_mask, word2_mask); | ||||
| 			if (pvt_id >= 0) | ||||
| 				return pvt_id; | ||||
| 
 | ||||
| 			word1_mask += 32; | ||||
| 			if ((word1_mask + word2_mask) >= module->max_instance) | ||||
| 				return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		word2_mask += 64; | ||||
| 		if (word2_mask >= module->max_instance) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * skl_get_pvt_id: generate a private id for use as module id | ||||
|  * | ||||
|  * @ctx: driver context | ||||
|  * @mconfig: module configuration data | ||||
|  * | ||||
|  * This generates a 128 bit private unique id for a module TYPE so that | ||||
|  * module instance is unique | ||||
|  */ | ||||
| int skl_get_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig) | ||||
| { | ||||
| 	struct uuid_module *module; | ||||
| 	uuid_le *uuid_mod; | ||||
| 	int pvt_id; | ||||
| 
 | ||||
| 	uuid_mod = (uuid_le *)mconfig->guid; | ||||
| 
 | ||||
| 	list_for_each_entry(module, &ctx->uuid_list, list) { | ||||
| 		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { | ||||
| 
 | ||||
| 			pvt_id = skl_pvtid_128(module); | ||||
| 			if (pvt_id >= 0) { | ||||
| 				module->instance_id[pvt_id] = | ||||
| 						mconfig->id.instance_id; | ||||
| 				return pvt_id; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_get_pvt_id); | ||||
| 
 | ||||
| /**
 | ||||
|  * skl_put_pvt_id: free up the private id allocated | ||||
|  * | ||||
|  * @ctx: driver context | ||||
|  * @mconfig: module configuration data | ||||
|  * | ||||
|  * This frees a 128 bit private unique id previously generated | ||||
|  */ | ||||
| int skl_put_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig) | ||||
| { | ||||
| 	int i; | ||||
| 	uuid_le *uuid_mod; | ||||
| 	struct uuid_module *module; | ||||
| 
 | ||||
| 	uuid_mod = (uuid_le *)mconfig->guid; | ||||
| 	list_for_each_entry(module, &ctx->uuid_list, list) { | ||||
| 		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { | ||||
| 
 | ||||
| 			if (mconfig->id.pvt_id != 0) | ||||
| 				i = (mconfig->id.pvt_id) / 64; | ||||
| 			else | ||||
| 				i = 0; | ||||
| 
 | ||||
| 			module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id)); | ||||
| 			mconfig->id.pvt_id = -1; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_put_pvt_id); | ||||
| 
 | ||||
| /*
 | ||||
|  * Parse the firmware binary to get the UUID, module id | ||||
|  * and loadable flags | ||||
|  */ | ||||
| int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) | ||||
| int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, | ||||
| 			unsigned int offset, int index) | ||||
| { | ||||
| 	struct adsp_fw_hdr *adsp_hdr; | ||||
| 	struct adsp_module_entry *mod_entry; | ||||
| 	int i, num_entry; | ||||
| 	int i, num_entry, size; | ||||
| 	uuid_le *uuid_bin; | ||||
| 	const char *buf; | ||||
| 	struct skl_sst *skl = ctx->thread_context; | ||||
| @ -158,8 +297,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) | ||||
| 	unsigned int safe_file; | ||||
| 
 | ||||
| 	/* Get the FW pointer to derive ADSP header */ | ||||
| 	stripped_fw.data = ctx->fw->data; | ||||
| 	stripped_fw.size = ctx->fw->size; | ||||
| 	stripped_fw.data = fw->data; | ||||
| 	stripped_fw.size = fw->size; | ||||
| 
 | ||||
| 	skl_dsp_strip_extended_manifest(&stripped_fw); | ||||
| 
 | ||||
| @ -210,8 +349,15 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) | ||||
| 		uuid_bin = (uuid_le *)mod_entry->uuid.id; | ||||
| 		memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); | ||||
| 
 | ||||
| 		module->id = i; | ||||
| 		module->id = (i | (index << 12)); | ||||
| 		module->is_loadable = mod_entry->type.load_type; | ||||
| 		module->max_instance = mod_entry->instance_max_count; | ||||
| 		size = sizeof(int) * mod_entry->instance_max_count; | ||||
| 		module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL); | ||||
| 		if (!module->instance_id) { | ||||
| 			kfree(module); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 
 | ||||
| 		list_add_tail(&module->list, &skl->uuid_list); | ||||
| 
 | ||||
|  | ||||
| @ -88,13 +88,15 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ctx->dev, | ||||
| 				"UUID parsing err: %d\n", ret); | ||||
| 		release_firmware(ctx->fw); | ||||
| 		skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); | ||||
| 		return ret; | ||||
| 	/* prase uuids on first boot */ | ||||
| 	if (skl->is_first_boot) { | ||||
| 		ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(ctx->dev, "UUID parsing err: %d\n", ret); | ||||
| 			release_firmware(ctx->fw); | ||||
| 			skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* check for extended manifest */ | ||||
| @ -105,13 +107,13 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) | ||||
| 
 | ||||
| 	ret = skl_dsp_boot(ctx); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); | ||||
| 		dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); | ||||
| 		goto skl_load_base_firmware_failed; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = skl_cldma_prepare(ctx); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(ctx->dev, "CL dma prepare failed : %d", ret); | ||||
| 		dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret); | ||||
| 		goto skl_load_base_firmware_failed; | ||||
| 	} | ||||
| 
 | ||||
| @ -484,26 +486,33 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | ||||
| 		return ret; | ||||
| 
 | ||||
| 	skl->cores.count = 2; | ||||
| 
 | ||||
| 	ret = sst->fw_ops.load_fw(sst); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(dev, "Load base fw failed : %d", ret); | ||||
| 		goto cleanup; | ||||
| 	} | ||||
| 
 | ||||
| 	skl_dsp_init_core_state(sst); | ||||
| 	skl->is_first_boot = true; | ||||
| 
 | ||||
| 	if (dsp) | ||||
| 		*dsp = skl; | ||||
| 
 | ||||
| 	return ret; | ||||
| 
 | ||||
| cleanup: | ||||
| 	skl_sst_dsp_cleanup(dev, skl); | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_sst_dsp_init); | ||||
| 
 | ||||
| int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct sst_dsp *sst = ctx->dsp; | ||||
| 
 | ||||
| 	ret = sst->fw_ops.load_fw(sst); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(dev, "Load base fw failed : %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	skl_dsp_init_core_state(sst); | ||||
| 	ctx->is_first_boot = false; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(skl_sst_init_fw); | ||||
| 
 | ||||
| void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) | ||||
| { | ||||
| 	skl_clear_module_table(ctx->dsp); | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -133,7 +133,7 @@ struct skl_i2s_config_blob { | ||||
| struct skl_dma_control { | ||||
| 	u32 node_id; | ||||
| 	u32 config_length; | ||||
| 	u32 config_data[1]; | ||||
| 	u32 config_data[0]; | ||||
| } __packed; | ||||
| 
 | ||||
| struct skl_cpr_cfg { | ||||
| @ -215,9 +215,20 @@ struct skl_module_fmt { | ||||
| 
 | ||||
| struct skl_module_cfg; | ||||
| 
 | ||||
| struct skl_mod_inst_map { | ||||
| 	u16 mod_id; | ||||
| 	u16 inst_id; | ||||
| }; | ||||
| 
 | ||||
| struct skl_kpb_params { | ||||
| 	u32 num_modules; | ||||
| 	struct skl_mod_inst_map map[0]; | ||||
| }; | ||||
| 
 | ||||
| struct skl_module_inst_id { | ||||
| 	u32 module_id; | ||||
| 	int module_id; | ||||
| 	u32 instance_id; | ||||
| 	int pvt_id; | ||||
| }; | ||||
| 
 | ||||
| enum skl_module_pin_state { | ||||
|  | ||||
| @ -80,7 +80,8 @@ enum skl_module_type { | ||||
| 	SKL_MODULE_TYPE_UPDWMIX, | ||||
| 	SKL_MODULE_TYPE_SRCINT, | ||||
| 	SKL_MODULE_TYPE_ALGO, | ||||
| 	SKL_MODULE_TYPE_BASE_OUTFMT | ||||
| 	SKL_MODULE_TYPE_BASE_OUTFMT, | ||||
| 	SKL_MODULE_TYPE_KPB, | ||||
| }; | ||||
| 
 | ||||
| enum skl_core_affinity { | ||||
| @ -148,78 +149,6 @@ enum skl_module_param_type { | ||||
| 	SKL_PARAM_BIND | ||||
| }; | ||||
| 
 | ||||
| struct skl_dfw_module_pin { | ||||
| 	u16 module_id; | ||||
| 	u16 instance_id; | ||||
| } __packed; | ||||
| 
 | ||||
| struct skl_dfw_module_fmt { | ||||
| 	u32 channels; | ||||
| 	u32 freq; | ||||
| 	u32 bit_depth; | ||||
| 	u32 valid_bit_depth; | ||||
| 	u32 ch_cfg; | ||||
| 	u32 interleaving_style; | ||||
| 	u32 sample_type; | ||||
| 	u32 ch_map; | ||||
| } __packed; | ||||
| 
 | ||||
| struct skl_dfw_module_caps { | ||||
| 	u32 set_params:2; | ||||
| 	u32 rsvd:30; | ||||
| 	u32 param_id; | ||||
| 	u32 caps_size; | ||||
| 	u32 caps[HDA_SST_CFG_MAX]; | ||||
| }; | ||||
| 
 | ||||
| struct skl_dfw_pipe { | ||||
| 	u8 pipe_id; | ||||
| 	u8 pipe_priority; | ||||
| 	u16 conn_type:4; | ||||
| 	u16 rsvd:4; | ||||
| 	u16 memory_pages:8; | ||||
| } __packed; | ||||
| 
 | ||||
| struct skl_dfw_module { | ||||
| 	u8 uuid[16]; | ||||
| 
 | ||||
| 	u16 module_id; | ||||
| 	u16 instance_id; | ||||
| 	u32 max_mcps; | ||||
| 	u32 mem_pages; | ||||
| 	u32 obs; | ||||
| 	u32 ibs; | ||||
| 	u32 vbus_id; | ||||
| 
 | ||||
| 	u32 max_in_queue:8; | ||||
| 	u32 max_out_queue:8; | ||||
| 	u32 time_slot:8; | ||||
| 	u32 core_id:4; | ||||
| 	u32 rsvd1:4; | ||||
| 
 | ||||
| 	u32 module_type:8; | ||||
| 	u32 conn_type:4; | ||||
| 	u32 dev_type:4; | ||||
| 	u32 hw_conn_type:4; | ||||
| 	u32 rsvd2:12; | ||||
| 
 | ||||
| 	u32 params_fixup:8; | ||||
| 	u32 converter:8; | ||||
| 	u32 input_pin_type:1; | ||||
| 	u32 output_pin_type:1; | ||||
| 	u32 is_dynamic_in_pin:1; | ||||
| 	u32 is_dynamic_out_pin:1; | ||||
| 	u32 is_loadable:1; | ||||
| 	u32 rsvd3:11; | ||||
| 
 | ||||
| 	struct skl_dfw_pipe pipe; | ||||
| 	struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE]; | ||||
| 	struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE]; | ||||
| 	struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; | ||||
| 	struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; | ||||
| 	struct skl_dfw_module_caps caps; | ||||
| } __packed; | ||||
| 
 | ||||
| struct skl_dfw_algo_data { | ||||
| 	u32 set_params:2; | ||||
| 	u32 rsvd:30; | ||||
| @ -228,4 +157,26 @@ struct skl_dfw_algo_data { | ||||
| 	char params[0]; | ||||
| } __packed; | ||||
| 
 | ||||
| #define LIB_NAME_LENGTH	128 | ||||
| #define HDA_MAX_LIB	16 | ||||
| 
 | ||||
| struct lib_info { | ||||
| 	char name[LIB_NAME_LENGTH]; | ||||
| } __packed; | ||||
| 
 | ||||
| struct skl_dfw_manifest { | ||||
| 	u32 lib_count; | ||||
| 	struct lib_info lib[HDA_MAX_LIB]; | ||||
| } __packed; | ||||
| 
 | ||||
| enum skl_tkn_dir { | ||||
| 	SKL_DIR_IN, | ||||
| 	SKL_DIR_OUT | ||||
| }; | ||||
| 
 | ||||
| enum skl_tuple_type { | ||||
| 	SKL_TYPE_TUPLE, | ||||
| 	SKL_TYPE_DATA | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	snd_hdac_ext_bus_parse_capabilities(ebus); | ||||
| 	snd_hdac_bus_parse_capabilities(bus); | ||||
| 
 | ||||
| 	if (skl_acquire_irq(ebus, 0) < 0) | ||||
| 		return -EBUSY; | ||||
| @ -684,7 +684,7 @@ static int skl_probe(struct pci_dev *pci, | ||||
| 	skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); | ||||
| 
 | ||||
| 	/* check if dsp is there */ | ||||
| 	if (ebus->ppcap) { | ||||
| 	if (bus->ppcap) { | ||||
| 		err = skl_machine_device_register(skl, | ||||
| 				  (void *)pci_id->driver_data); | ||||
| 		if (err < 0) | ||||
| @ -698,7 +698,7 @@ static int skl_probe(struct pci_dev *pci, | ||||
| 		skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; | ||||
| 
 | ||||
| 	} | ||||
| 	if (ebus->mlcap) | ||||
| 	if (bus->mlcap) | ||||
| 		snd_hdac_ext_bus_get_ml_capabilities(ebus); | ||||
| 
 | ||||
| 	/* create device for soc dmic */ | ||||
|  | ||||
| @ -105,6 +105,7 @@ struct skl_dsp_ops { | ||||
| 			int irq, const char *fw_name, | ||||
| 			struct skl_dsp_loader_ops loader_ops, | ||||
| 			struct skl_sst **skl_sst); | ||||
| 	int (*init_fw)(struct device *dev, struct skl_sst *ctx); | ||||
| 	void (*cleanup)(struct device *dev, struct skl_sst *ctx); | ||||
| }; | ||||
| 
 | ||||
| @ -123,4 +124,5 @@ int skl_free_dsp(struct skl *skl); | ||||
| int skl_suspend_dsp(struct skl *skl); | ||||
| int skl_resume_dsp(struct skl *skl); | ||||
| void skl_cleanup_resources(struct skl *skl); | ||||
| const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); | ||||
| #endif /* __SOUND_SOC_SKL_H */ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user