Commit 4a2447b4 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/intel' into asoc-next

parents 00f12dbd 0730bd2e
...@@ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; ...@@ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_REG_SD_BDLPL 0x18 #define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c #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 */ /* Haswell/Broadwell display HD-A controller Extended Mode registers */
#define AZX_REG_HSW_EM4 0x100c #define AZX_REG_HSW_EM4 0x100c
#define AZX_REG_HSW_EM5 0x1010 #define AZX_REG_HSW_EM5 0x1010
...@@ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; ...@@ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* Interval used to calculate the iterating register offset */ /* Interval used to calculate the iterating register offset */
#define AZX_DRSM_INTERVAL 0x08 #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 * helpers to read the stream position
*/ */
......
...@@ -245,6 +245,12 @@ struct hdac_rb { ...@@ -245,6 +245,12 @@ struct hdac_rb {
/* /*
* HD-audio bus base driver * 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 hdac_bus {
struct device *dev; struct device *dev;
...@@ -256,6 +262,12 @@ struct hdac_bus { ...@@ -256,6 +262,12 @@ struct hdac_bus {
void __iomem *remap_addr; void __iomem *remap_addr;
int irq; int irq;
void __iomem *ppcap;
void __iomem *spbcap;
void __iomem *mlcap;
void __iomem *gtscap;
void __iomem *drsmcap;
/* codec linked list */ /* codec linked list */
struct list_head codec_list; struct list_head codec_list;
unsigned int num_codecs; unsigned int num_codecs;
...@@ -335,6 +347,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) ...@@ -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_send_cmd(struct hdac_bus *bus, unsigned int val);
int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned int *res); unsigned int *res);
int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus);
int snd_hdac_link_power(struct hdac_device *codec, bool enable); int snd_hdac_link_power(struct hdac_device *codec, bool enable);
bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset); bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
......
...@@ -8,11 +8,6 @@ ...@@ -8,11 +8,6 @@
* *
* @bus: hdac bus * @bus: hdac bus
* @num_streams: streams supported * @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 * @hlink_list: link list of HDA links
* @lock: lock for link mgmt * @lock: lock for link mgmt
* @cmd_dma_state: state of cmd DMAs: CORB and RIRB * @cmd_dma_state: state of cmd DMAs: CORB and RIRB
...@@ -22,12 +17,6 @@ struct hdac_ext_bus { ...@@ -22,12 +17,6 @@ struct hdac_ext_bus {
int num_streams; int num_streams;
int idx; int idx;
void __iomem *ppcap;
void __iomem *spbcap;
void __iomem *mlcap;
void __iomem *gtscap;
void __iomem *drsmcap;
struct list_head hlink_list; struct list_head hlink_list;
struct mutex lock; struct mutex lock;
...@@ -54,7 +43,6 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); ...@@ -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) \ #define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \
HDA_CODEC_REV_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_enable(struct hdac_ext_bus *chip, bool enable);
void snd_hdac_ext_bus_ppcap_int_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 ...@@ -13,3 +13,4 @@ header-y += sb16_csp.h
header-y += sfnt_info.h header-y += sfnt_info.h
header-y += tlv.h header-y += tlv.h
header-y += usb_stream.h header-y += usb_stream.h
header-y += snd_sst_tokens.h
/*
* 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 @@ ...@@ -29,81 +29,6 @@
*/ */
#define HDAC_MAX_CAPS 10 #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 * processing pipe helpers - these helpers are useful for dealing with HDA
* new capability of processing pipelines * new capability of processing pipelines
...@@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) ...@@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->ppcap) { if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL"); dev_err(bus->dev, "Address of PP capability is NULL");
return; return;
} }
if (enable) 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 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); 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) ...@@ -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; struct hdac_bus *bus = &ebus->bus;
if (!ebus->ppcap) { if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL\n"); dev_err(bus->dev, "Address of PP capability is NULL\n");
return; return;
} }
if (enable) 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 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); 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) ...@@ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
struct hdac_ext_link *hlink; struct hdac_ext_link *hlink;
struct hdac_bus *bus = &ebus->bus; 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); 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) ...@@ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
return -ENOMEM; return -ENOMEM;
hlink->index = idx; hlink->index = idx;
hlink->bus = bus; hlink->bus = bus;
hlink->ml_addr = ebus->mlcap + AZX_ML_BASE + hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
(AZX_ML_INTERVAL * idx); (AZX_ML_INTERVAL * idx);
hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); 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, ...@@ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (ebus->ppcap) { if (bus->ppcap) {
stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE + stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
AZX_PPHC_INTERVAL * idx; 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_MULTI * ebus->num_streams +
AZX_PPLC_INTERVAL * idx; AZX_PPLC_INTERVAL * idx;
} }
if (ebus->spbcap) { if (bus->spbcap) {
stream->spib_addr = ebus->spbcap + AZX_SPB_BASE + stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx + AZX_SPB_INTERVAL * idx +
AZX_SPB_SPIB; 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_INTERVAL * idx +
AZX_SPB_MAXFIFO; AZX_SPB_MAXFIFO;
} }
if (ebus->drsmcap) if (bus->drsmcap)
stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE + stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
AZX_DRSM_INTERVAL * idx; AZX_DRSM_INTERVAL * idx;
stream->decoupled = false; stream->decoupled = false;
...@@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, ...@@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
spin_lock_irq(&bus->reg_lock); spin_lock_irq(&bus->reg_lock);
if (decouple) 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)); AZX_PPCTL_PROCEN(hstream->index));
else 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); AZX_PPCTL_PROCEN(hstream->index), 0);
stream->decoupled = decouple; stream->decoupled = decouple;
spin_unlock_irq(&bus->reg_lock); spin_unlock_irq(&bus->reg_lock);
...@@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, ...@@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
struct hdac_stream *stream = NULL; struct hdac_stream *stream = NULL;
struct hdac_bus *hbus = &ebus->bus; struct hdac_bus *hbus = &ebus->bus;
if (!ebus->ppcap) { if (!hbus->ppcap) {
dev_err(hbus->dev, "stream type not supported\n"); dev_err(hbus->dev, "stream type not supported\n");
return NULL; return NULL;
} }
...@@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, ...@@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
struct hdac_stream *stream = NULL; struct hdac_stream *stream = NULL;
struct hdac_bus *hbus = &ebus->bus; struct hdac_bus *hbus = &ebus->bus;
if (!ebus->ppcap) { if (!hbus->ppcap) {
dev_err(hbus->dev, "stream type not supported\n"); dev_err(hbus->dev, "stream type not supported\n");
return NULL; return NULL;
} }
...@@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, ...@@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
u32 register_mask = 0; u32 register_mask = 0;
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->spbcap) { if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL"); dev_err(bus->dev, "Address of SPB capability is NULL");
return; return;
} }
mask |= (1 << index); mask |= (1 << index);
register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL); register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL);
mask |= register_mask; mask |= register_mask;
if (enable) 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 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); 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, ...@@ -452,7 +452,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->spbcap) { if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL"); dev_err(bus->dev, "Address of SPB capability is NULL");
return -EINVAL; return -EINVAL;
} }
...@@ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, ...@@ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->spbcap) { if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL"); dev_err(bus->dev, "Address of SPB capability is NULL");
return -EINVAL; return -EINVAL;
} }
...@@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, ...@@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
u32 register_mask = 0; u32 register_mask = 0;
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->drsmcap) { if (!bus->drsmcap) {
dev_err(bus->dev, "Address of DRSM capability is NULL"); dev_err(bus->dev, "Address of DRSM capability is NULL");
return; return;
} }
mask |= (1 << index); mask |= (1 << index);
register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL); register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL);
mask |= register_mask; mask |= register_mask;
if (enable) 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 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); 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, ...@@ -544,7 +544,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->drsmcap) { if (!bus->drsmcap) {
dev_err(bus->dev, "Address of DRSM capability is NULL"); dev_err(bus->dev, "Address of DRSM capability is NULL");
return -EINVAL; return -EINVAL;
} }
......
...@@ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, ...@@ -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); 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 * Lowlevel interface
*/ */
......
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#ifdef CONFIG_X86
/* for art-tsc conversion */
#include <asm/tsc.h>
#endif
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
#include "hda_controller.h" #include "hda_controller.h"
...@@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) ...@@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
azx_get_position(chip, azx_dev)); 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, static int azx_get_time_info(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts, struct timespec *system_ts, struct timespec *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report) struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
{ {
struct azx_dev *azx_dev = get_azx_dev(substream); 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; u64 nsec;
if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && 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, ...@@ -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_report = 1; /* rest of structure is valid */
audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ 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; audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
}
return 0; return 0;
} }
...@@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) ...@@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
goto unlock; goto unlock;
} }
runtime->private_data = azx_dev; 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 = azx_pcm_hw;
runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max; runtime->hw.channels_max = hinfo->channels_max;
......
...@@ -159,6 +159,9 @@ struct azx { ...@@ -159,6 +159,9 @@ struct azx {
unsigned int region_requested:1; unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by vga_switcheroo */ unsigned int disabled:1; /* disabled by vga_switcheroo */
/* GTS present */
unsigned int gts_present:1;
#ifdef CONFIG_SND_HDA_DSP_LOADER #ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev; struct azx_dev saved_azx_dev;
#endif #endif
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
/* for snoop control */ /* for snoop control */
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#endif #endif
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
...@@ -1663,6 +1664,22 @@ static int azx_first_init(struct azx *chip) ...@@ -1663,6 +1664,22 @@ static int azx_first_init(struct azx *chip)
return -ENXIO; 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->msi) {
if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
dev_dbg(card->dev, "Disabling 64bit MSI\n"); dev_dbg(card->dev, "Disabling 64bit MSI\n");
......
...@@ -1870,6 +1870,9 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, ...@@ -1870,6 +1870,9 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
case RT5640_SCLK_S_PLL1: case RT5640_SCLK_S_PLL1:
reg_val |= RT5640_SCLK_SRC_PLL1; reg_val |= RT5640_SCLK_SRC_PLL1;
break; break;
case RT5640_SCLK_S_RCCLK:
reg_val |= RT5640_SCLK_SRC_RCCLK;
break;
default: default:
dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL; return -EINVAL;
......
...@@ -984,6 +984,7 @@ ...@@ -984,6 +984,7 @@
#define RT5640_SCLK_SRC_SFT 14 #define RT5640_SCLK_SRC_SFT 14
#define RT5640_SCLK_SRC_MCLK (0x0 << 14) #define RT5640_SCLK_SRC_MCLK (0x0 << 14)
#define RT5640_SCLK_SRC_PLL1 (0x1 << 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_MASK (0x3 << 12)
#define RT5640_PLL1_SRC_SFT 12 #define RT5640_PLL1_SRC_SFT 12
#define RT5640_PLL1_SRC_MCLK (0x0 << 12) #define RT5640_PLL1_SRC_MCLK (0x0 << 12)
......
...@@ -25,6 +25,7 @@ config SND_SST_IPC_ACPI ...@@ -25,6 +25,7 @@ config SND_SST_IPC_ACPI
tristate tristate
select SND_SST_IPC select SND_SST_IPC
select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST
select IOSF_MBI
config SND_SOC_INTEL_SST config SND_SOC_INTEL_SST
tristate tristate
...@@ -120,6 +121,17 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH ...@@ -120,6 +121,17 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
This adds audio driver for Intel Baytrail platform based boards This adds audio driver for Intel Baytrail platform based boards
with the MAX98090 audio codec. 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 config SND_SOC_INTEL_BROADWELL_MACH
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
......
/* /*
* sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
* *
* Copyright (C) 2013-14 Intel Corp * 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, ...@@ -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 */ /* Look up table to convert MIXER SW bit regs to SWM inputs */
static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { 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_CODEC0] = SST_SWM_IN_CODEC0,
[SST_IP_CODEC1] = SST_SWM_IN_CODEC1, [SST_IP_CODEC1] = SST_SWM_IN_CODEC1,
[SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP,
...@@ -674,6 +675,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, ...@@ -674,6 +675,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
/* SBA mixers - 16 inputs */ /* SBA mixers - 16 inputs */
#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \ #define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \
static const struct snd_kcontrol_new 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_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("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), \ 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, ...@@ -684,6 +686,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
} }
#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \ #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_in0 Switch", "codec_in0" }, \
{ mix_name, "codec_in1 Switch", "codec_in1" }, \ { mix_name, "codec_in1 Switch", "codec_in1" }, \
{ mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \ { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \
...@@ -713,6 +716,7 @@ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls); ...@@ -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_voip_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_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_codec1_controls);
SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls);
/* /*
* sst_handle_vb_timer - Start/Stop the DSP scheduler * sst_handle_vb_timer - Start/Stop the DSP scheduler
...@@ -931,17 +935,26 @@ void sst_fill_ssp_defaults(struct snd_soc_dai *dai) ...@@ -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) int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
{ {
struct sst_data *drv = snd_soc_dai_get_drvdata(dai); 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); 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); SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);
drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP; drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;
drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
- sizeof(struct sst_dsp_header); - sizeof(struct sst_dsp_header);
config = &sst_ssp_configs; drv->ssp_cmd.selection = ssp_id;
dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id);
if (enable) if (enable)
drv->ssp_cmd.switch_state = SST_SWITCH_ON; drv->ssp_cmd.switch_state = SST_SWITCH_ON;
...@@ -1047,8 +1060,10 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w, ...@@ -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[] = { 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_in0", sst_set_be_modules),
SST_AIF_IN("codec_in1", 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_out0", sst_set_be_modules),
SST_AIF_OUT("codec_out1", 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[] = { ...@@ -1103,6 +1118,9 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
sst_mix_codec0_controls, sst_swm_mixer_event), 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_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_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[] = { static const struct snd_soc_dapm_route intercon[] = {
...@@ -1148,6 +1166,9 @@ 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"), SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
{"codec_out1", NULL, "codec_out1 mix 0"}, {"codec_out1", NULL, "codec_out1 mix 0"},
SST_SBA_MIXER_GRAPH_MAP("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[] = { static const char * const slot_names[] = {
...@@ -1217,6 +1238,9 @@ static const struct snd_kcontrol_new sst_gain_controls[] = { ...@@ -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("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_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_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 #define SST_GAIN_NUM_CONTROLS 3
......
...@@ -35,6 +35,8 @@ enum { ...@@ -35,6 +35,8 @@ enum {
/* define a bit for each mixer input */ /* define a bit for each mixer input */
#define SST_MIX_IP(x) (x) #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_CODEC0 SST_MIX_IP(2)
#define SST_IP_CODEC1 SST_MIX_IP(3) #define SST_IP_CODEC1 SST_MIX_IP(3)
#define SST_IP_LOOP0 SST_MIX_IP(4) #define SST_IP_LOOP0 SST_MIX_IP(4)
...@@ -63,6 +65,7 @@ enum { ...@@ -63,6 +65,7 @@ enum {
* Audio DSP Path Ids. Specified by the audio DSP FW * Audio DSP Path Ids. Specified by the audio DSP FW
*/ */
enum sst_path_index { 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_OUT0 = (0x02 << SST_PATH_ID_SHIFT),
SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT),
...@@ -80,6 +83,7 @@ enum sst_path_index { ...@@ -80,6 +83,7 @@ enum sst_path_index {
/* Start of input paths */ /* 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_IN0 = (0x82 << SST_PATH_ID_SHIFT),
SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT),
...@@ -105,6 +109,7 @@ enum sst_path_index { ...@@ -105,6 +109,7 @@ enum sst_path_index {
* path IDs * path IDs
*/ */
enum sst_swm_inputs { 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_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_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), SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR),
...@@ -124,6 +129,7 @@ enum sst_swm_inputs { ...@@ -124,6 +129,7 @@ enum sst_swm_inputs {
* path IDs * path IDs
*/ */
enum sst_swm_outputs { 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_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_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), 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) ...@@ -190,7 +190,8 @@ int sst_driver_ops(struct intel_sst_drv *sst)
default: default:
dev_err(sst->dev, 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; return -EINVAL;
}; };
} }
...@@ -441,7 +442,7 @@ static int intel_sst_suspend(struct device *dev) ...@@ -441,7 +442,7 @@ static int intel_sst_suspend(struct device *dev)
struct stream_info *stream = &ctx->streams[i]; struct stream_info *stream = &ctx->streams[i];
if (stream->status == STREAM_RUNNING) { 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; return -EBUSY;
} }
} }
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#include <acpi/platform/aclinux.h> #include <acpi/platform/aclinux.h>
#include <acpi/actypes.h> #include <acpi/actypes.h>
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include <asm/cpu_device_id.h>
#include <asm/iosf_mbi.h>
#include "../sst-mfld-platform.h" #include "../sst-mfld-platform.h"
#include "../../common/sst-dsp.h" #include "../../common/sst-dsp.h"
#include "../../common/sst-acpi.h" #include "../../common/sst-acpi.h"
...@@ -113,6 +115,28 @@ static const struct sst_res_info byt_rvp_res_info = { ...@@ -113,6 +115,28 @@ static const struct sst_res_info byt_rvp_res_info = {
.acpi_ipc_irq_index = 5, .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 = { static struct sst_platform_info byt_rvp_platform_data = {
.probe_data = &byt_fwparse_info, .probe_data = &byt_fwparse_info,
.ipc_info = &byt_ipc_info, .ipc_info = &byt_ipc_info,
...@@ -142,7 +166,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -142,7 +166,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
rsrc = platform_get_resource(pdev, IORESOURCE_MEM, rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
ctx->pdata->res_info->acpi_lpe_res_index); ctx->pdata->res_info->acpi_lpe_res_index);
if (!rsrc) { if (!rsrc) {
dev_err(ctx->dev, "Invalid SHIM base from IFWI"); dev_err(ctx->dev, "Invalid SHIM base from IFWI\n");
return -EIO; return -EIO;
} }
dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start, 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) ...@@ -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->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
ctx->pdata->res_info->iram_size); ctx->pdata->res_info->iram_size);
if (!ctx->iram) { if (!ctx->iram) {
dev_err(ctx->dev, "unable to map IRAM"); dev_err(ctx->dev, "unable to map IRAM\n");
return -EIO; return -EIO;
} }
...@@ -164,7 +188,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -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->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
ctx->pdata->res_info->dram_size); ctx->pdata->res_info->dram_size);
if (!ctx->dram) { if (!ctx->dram) {
dev_err(ctx->dev, "unable to map DRAM"); dev_err(ctx->dev, "unable to map DRAM\n");
return -EIO; return -EIO;
} }
...@@ -173,7 +197,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -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->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
ctx->pdata->res_info->shim_size); ctx->pdata->res_info->shim_size);
if (!ctx->shim) { if (!ctx->shim) {
dev_err(ctx->dev, "unable to map SHIM"); dev_err(ctx->dev, "unable to map SHIM\n");
return -EIO; return -EIO;
} }
...@@ -186,7 +210,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -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->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
ctx->pdata->res_info->mbox_size); ctx->pdata->res_info->mbox_size);
if (!ctx->mailbox) { if (!ctx->mailbox) {
dev_err(ctx->dev, "unable to map mailbox"); dev_err(ctx->dev, "unable to map mailbox\n");
return -EIO; return -EIO;
} }
...@@ -196,7 +220,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -196,7 +220,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
rsrc = platform_get_resource(pdev, IORESOURCE_MEM, rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
ctx->pdata->res_info->acpi_ddr_index); ctx->pdata->res_info->acpi_ddr_index);
if (!rsrc) { if (!rsrc) {
dev_err(ctx->dev, "Invalid DDR base from IFWI"); dev_err(ctx->dev, "Invalid DDR base from IFWI\n");
return -EIO; return -EIO;
} }
ctx->ddr_base = rsrc->start; ctx->ddr_base = rsrc->start;
...@@ -205,7 +229,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -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, ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
resource_size(rsrc)); resource_size(rsrc));
if (!ctx->ddr) { if (!ctx->ddr) {
dev_err(ctx->dev, "unable to map DDR"); dev_err(ctx->dev, "unable to map DDR\n");
return -EIO; return -EIO;
} }
...@@ -215,6 +239,46 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) ...@@ -215,6 +239,46 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
return 0; 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) static int sst_acpi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -226,11 +290,12 @@ static int sst_acpi_probe(struct platform_device *pdev) ...@@ -226,11 +290,12 @@ static int sst_acpi_probe(struct platform_device *pdev)
struct platform_device *plat_dev; struct platform_device *plat_dev;
struct sst_platform_info *pdata; struct sst_platform_info *pdata;
unsigned int dev_id; unsigned int dev_id;
bool bytcr = false;
id = acpi_match_device(dev->driver->acpi_match_table, dev); id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id) if (!id)
return -ENODEV; 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 = (struct sst_acpi_mach *)id->driver_data;
mach = sst_acpi_find_machine(mach); mach = sst_acpi_find_machine(mach);
...@@ -251,6 +316,18 @@ static int sst_acpi_probe(struct platform_device *pdev) ...@@ -251,6 +316,18 @@ static int sst_acpi_probe(struct platform_device *pdev)
dev_dbg(dev, "ACPI device id: %x\n", dev_id); 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, plat_dev = platform_device_register_data(dev, pdata->platform, -1,
NULL, 0); NULL, 0);
if (IS_ERR(plat_dev)) { if (IS_ERR(plat_dev)) {
...@@ -271,10 +348,6 @@ static int sst_acpi_probe(struct platform_device *pdev) ...@@ -271,10 +348,6 @@ static int sst_acpi_probe(struct platform_device *pdev)
return PTR_ERR(mdev); return PTR_ERR(mdev);
} }
ret = sst_alloc_drv_context(&ctx, dev, dev_id);
if (ret < 0)
return ret;
/* Fill sst platform data */ /* Fill sst platform data */
ctx->pdata = pdata; ctx->pdata = pdata;
strcpy(ctx->firmware_name, mach->fw_filename); strcpy(ctx->firmware_name, mach->fw_filename);
......
snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.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-broadwell-objs := broadwell.o
snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
snd-soc-sst-bxt-rt298-objs := bxt_rt298.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 ...@@ -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_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_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_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_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_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 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
......
/*
* 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(&params->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 { ...@@ -37,6 +37,7 @@ enum {
BXT_DPCM_AUDIO_PB = 0, BXT_DPCM_AUDIO_PB = 0,
BXT_DPCM_AUDIO_CP, BXT_DPCM_AUDIO_CP,
BXT_DPCM_AUDIO_REF_CP, BXT_DPCM_AUDIO_REF_CP,
BXT_DPCM_AUDIO_DMIC_CP,
BXT_DPCM_AUDIO_HDMI1_PB, BXT_DPCM_AUDIO_HDMI1_PB,
BXT_DPCM_AUDIO_HDMI2_PB, BXT_DPCM_AUDIO_HDMI2_PB,
BXT_DPCM_AUDIO_HDMI3_PB, BXT_DPCM_AUDIO_HDMI3_PB,
...@@ -252,10 +253,56 @@ static struct snd_soc_ops broxton_da7219_ops = { ...@@ -252,10 +253,56 @@ static struct snd_soc_ops broxton_da7219_ops = {
.hw_free = broxton_da7219_hw_free, .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 */ /* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_dais[] = { static struct snd_soc_dai_link broxton_dais[] = {
/* Front End DAI links */ /* Front End DAI links */
[BXT_DPCM_AUDIO_PB] [BXT_DPCM_AUDIO_PB] =
{ {
.name = "Bxt Audio Port", .name = "Bxt Audio Port",
.stream_name = "Audio", .stream_name = "Audio",
...@@ -271,7 +318,7 @@ static struct snd_soc_dai_link broxton_dais[] = { ...@@ -271,7 +318,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.dpcm_playback = 1, .dpcm_playback = 1,
.ops = &broxton_da7219_fe_ops, .ops = &broxton_da7219_fe_ops,
}, },
[BXT_DPCM_AUDIO_CP] [BXT_DPCM_AUDIO_CP] =
{ {
.name = "Bxt Audio Capture Port", .name = "Bxt Audio Capture Port",
.stream_name = "Audio Record", .stream_name = "Audio Record",
...@@ -286,7 +333,7 @@ static struct snd_soc_dai_link broxton_dais[] = { ...@@ -286,7 +333,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.dpcm_capture = 1, .dpcm_capture = 1,
.ops = &broxton_da7219_fe_ops, .ops = &broxton_da7219_fe_ops,
}, },
[BXT_DPCM_AUDIO_REF_CP] [BXT_DPCM_AUDIO_REF_CP] =
{ {
.name = "Bxt Audio Reference cap", .name = "Bxt Audio Reference cap",
.stream_name = "Refcap", .stream_name = "Refcap",
...@@ -299,8 +346,23 @@ static struct snd_soc_dai_link broxton_dais[] = { ...@@ -299,8 +346,23 @@ static struct snd_soc_dai_link broxton_dais[] = {
.ignore_suspend = 1, .ignore_suspend = 1,
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
.ops = &broxton_refcap_ops,
},
[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] [BXT_DPCM_AUDIO_HDMI1_PB] =
{ {
.name = "Bxt HDMI Port1", .name = "Bxt HDMI Port1",
.stream_name = "Hdmi1", .stream_name = "Hdmi1",
...@@ -313,7 +375,7 @@ static struct snd_soc_dai_link broxton_dais[] = { ...@@ -313,7 +375,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
}, },
[BXT_DPCM_AUDIO_HDMI2_PB] [BXT_DPCM_AUDIO_HDMI2_PB] =
{ {
.name = "Bxt HDMI Port2", .name = "Bxt HDMI Port2",
.stream_name = "Hdmi2", .stream_name = "Hdmi2",
...@@ -326,7 +388,7 @@ static struct snd_soc_dai_link broxton_dais[] = { ...@@ -326,7 +388,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
}, },
[BXT_DPCM_AUDIO_HDMI3_PB] [BXT_DPCM_AUDIO_HDMI3_PB] =
{ {
.name = "Bxt HDMI Port3", .name = "Bxt HDMI Port3",
.stream_name = "Hdmi3", .stream_name = "Hdmi3",
...@@ -382,6 +444,7 @@ static struct snd_soc_dai_link broxton_dais[] = { ...@@ -382,6 +444,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
.codec_dai_name = "dmic-hifi", .codec_dai_name = "dmic-hifi",
.platform_name = "0000:00:0e.0", .platform_name = "0000:00:0e.0",
.ignore_suspend = 1, .ignore_suspend = 1,
.be_hw_params_fixup = broxton_dmic_fixup,
.dpcm_capture = 1, .dpcm_capture = 1,
.no_pcm = 1, .no_pcm = 1,
}, },
......
...@@ -271,7 +271,7 @@ static const struct snd_soc_ops broxton_rt286_fe_ops = { ...@@ -271,7 +271,7 @@ static const struct snd_soc_ops broxton_rt286_fe_ops = {
/* broxton digital audio interface glue - connects codec <--> CPU */ /* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_rt298_dais[] = { static struct snd_soc_dai_link broxton_rt298_dais[] = {
/* Front End DAI links */ /* Front End DAI links */
[BXT_DPCM_AUDIO_PB] [BXT_DPCM_AUDIO_PB] =
{ {
.name = "Bxt Audio Port", .name = "Bxt Audio Port",
.stream_name = "Audio", .stream_name = "Audio",
...@@ -286,7 +286,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { ...@@ -286,7 +286,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.dpcm_playback = 1, .dpcm_playback = 1,
.ops = &broxton_rt286_fe_ops, .ops = &broxton_rt286_fe_ops,
}, },
[BXT_DPCM_AUDIO_CP] [BXT_DPCM_AUDIO_CP] =
{ {
.name = "Bxt Audio Capture Port", .name = "Bxt Audio Capture Port",
.stream_name = "Audio Record", .stream_name = "Audio Record",
...@@ -300,7 +300,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { ...@@ -300,7 +300,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.dpcm_capture = 1, .dpcm_capture = 1,
.ops = &broxton_rt286_fe_ops, .ops = &broxton_rt286_fe_ops,
}, },
[BXT_DPCM_AUDIO_REF_CP] [BXT_DPCM_AUDIO_REF_CP] =
{ {
.name = "Bxt Audio Reference cap", .name = "Bxt Audio Reference cap",
.stream_name = "refcap", .stream_name = "refcap",
...@@ -313,7 +313,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { ...@@ -313,7 +313,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
}, },
[BXT_DPCM_AUDIO_DMIC_CP] [BXT_DPCM_AUDIO_DMIC_CP] =
{ {
.name = "Bxt Audio DMIC cap", .name = "Bxt Audio DMIC cap",
.stream_name = "dmiccap", .stream_name = "dmiccap",
...@@ -327,7 +327,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { ...@@ -327,7 +327,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.dynamic = 1, .dynamic = 1,
.ops = &broxton_dmic_ops, .ops = &broxton_dmic_ops,
}, },
[BXT_DPCM_AUDIO_HDMI1_PB] [BXT_DPCM_AUDIO_HDMI1_PB] =
{ {
.name = "Bxt HDMI Port1", .name = "Bxt HDMI Port1",
.stream_name = "Hdmi1", .stream_name = "Hdmi1",
...@@ -340,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { ...@@ -340,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
}, },
[BXT_DPCM_AUDIO_HDMI2_PB] [BXT_DPCM_AUDIO_HDMI2_PB] =
{ {
.name = "Bxt HDMI Port2", .name = "Bxt HDMI Port2",
.stream_name = "Hdmi2", .stream_name = "Hdmi2",
...@@ -353,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { ...@@ -353,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
}, },
[BXT_DPCM_AUDIO_HDMI3_PB] [BXT_DPCM_AUDIO_HDMI3_PB] =
{ {
.name = "Bxt HDMI Port3", .name = "Bxt HDMI Port3",
.stream_name = "Hdmi3", .stream_name = "Hdmi3",
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/slab.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.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
...@@ -31,42 +34,153 @@ ...@@ -31,42 +34,153 @@
#include "../../codecs/rt5640.h" #include "../../codecs/rt5640.h"
#include "../atom/sst-atom-controls.h" #include "../atom/sst-atom-controls.h"
#include "../common/sst-acpi.h" #include "../common/sst-acpi.h"
#include "../common/sst-dsp.h"
enum { enum {
BYT_RT5640_DMIC1_MAP, BYT_RT5640_DMIC1_MAP,
BYT_RT5640_DMIC2_MAP, BYT_RT5640_DMIC2_MAP,
BYT_RT5640_IN1_MAP, BYT_RT5640_IN1_MAP,
BYT_RT5640_IN3_MAP,
}; };
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) #define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
#define BYT_RT5640_DMIC_EN BIT(16) #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 | 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[] = { static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL),
SND_SOC_DAPM_SPK("Speaker", 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[] = { static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
{"AIF1 Playback", NULL, "ssp2 Tx"}, {"Headphone", NULL, "Platform Clock"},
{"ssp2 Tx", NULL, "codec_out0"}, {"Headset Mic", NULL, "Platform Clock"},
{"ssp2 Tx", NULL, "codec_out1"}, {"Internal Mic", NULL, "Platform Clock"},
{"codec_in0", NULL, "ssp2 Rx"}, {"Speaker", NULL, "Platform Clock"},
{"codec_in1", NULL, "ssp2 Rx"},
{"ssp2 Rx", NULL, "AIF1 Capture"},
{"Headset Mic", NULL, "MICBIAS1"}, {"Headset Mic", NULL, "MICBIAS1"},
{"IN2P", NULL, "Headset Mic"}, {"IN2P", NULL, "Headset Mic"},
{"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOL"},
{"Headphone", NULL, "HPOR"}, {"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[] = { 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[] = { ...@@ -82,6 +196,59 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
{"IN1P", NULL, "Internal Mic"}, {"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[] = { static const struct snd_kcontrol_new byt_rt5640_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Headset Mic"),
...@@ -96,19 +263,46 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, ...@@ -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; struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret; int ret;
snd_soc_dai_set_bclk_ratio(codec_dai, 50);
ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
params_rate(params) * 512, params_rate(params) * 512,
SND_SOC_CLOCK_IN); SND_SOC_CLOCK_IN);
if (ret < 0) { if (ret < 0) {
dev_err(rtd->dev, "can't set codec clock %d\n", ret); dev_err(rtd->dev, "can't set codec clock %d\n", ret);
return ret; return ret;
} }
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) {
params_rate(params) * 50, /* use bitclock as PLL input */
params_rate(params) * 512); 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) { if (ret < 0) {
dev_err(rtd->dev, "can't set codec pll: %d\n", ret); dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
return ret; return ret;
...@@ -127,27 +321,73 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { ...@@ -127,27 +321,73 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
{ {
.callback = byt_rt5640_quirk_cb, .callback = byt_rt5640_quirk_cb,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
},
.driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
BYT_RT5640_MCLK_EN),
},
{
.callback = byt_rt5640_quirk_cb,
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"),
}, },
.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, .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, .callback = byt_rt5640_quirk_cb,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
}, },
.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | .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), BYT_RT5640_DMIC_EN),
}, },
{ {
.callback = byt_rt5640_quirk_cb, .callback = byt_rt5640_quirk_cb,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), 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) ...@@ -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_codec *codec = runtime->codec;
struct snd_soc_card *card = runtime->card; struct snd_soc_card *card = runtime->card;
const struct snd_soc_dapm_route *custom_map; const struct snd_soc_dapm_route *custom_map;
struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
int num_routes; int num_routes;
card->dapm.idle_bias_off = true; card->dapm.idle_bias_off = true;
rt5640_sel_asrc_clk_src(codec, rt5640_sel_asrc_clk_src(codec,
RT5640_DA_STEREO_FILTER | 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); RT5640_CLK_SEL_ASRC);
ret = snd_soc_add_card_controls(card, byt_rt5640_controls, 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) ...@@ -179,6 +424,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
custom_map = byt_rt5640_intmic_in1_map; custom_map = byt_rt5640_intmic_in1_map;
num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
break; 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: case BYT_RT5640_DMIC2_MAP:
custom_map = byt_rt5640_intmic_dmic2_map; custom_map = byt_rt5640_intmic_dmic2_map;
num_routes = ARRAY_SIZE(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) ...@@ -192,6 +441,43 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
if (ret) if (ret)
return 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) { if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
ret = rt5640_dmic_enable(codec, 0, 0); ret = rt5640_dmic_enable(codec, 0, 0);
if (ret) if (ret)
...@@ -201,6 +487,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) ...@@ -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, "Headphone");
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); 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; return ret;
} }
...@@ -221,34 +531,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, ...@@ -221,34 +531,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_CHANNELS); SNDRV_PCM_HW_PARAM_CHANNELS);
int ret; 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; rate->min = rate->max = 48000;
channels->min = channels->max = 2; channels->min = channels->max = 2;
/* set SSP2 to 24-bit */ if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
/* 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);
* Default mode for SSP configuration is TDM 4 slot, override config if (ret < 0) {
* with explicit setting to I2S 2ch 24-bit. The word length is set with dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
* dai_set_tdm_slot() since there is no other API exposed return ret;
*/ }
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); } else {
if (ret < 0) {
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); /* set SSP2 to 24-bit */
return ret; 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;
}
}
return 0; return 0;
} }
...@@ -305,10 +644,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { ...@@ -305,10 +644,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
{ {
.name = "SSP2-Codec", .name = "SSP2-Codec",
.id = 1, .id = 1,
.cpu_dai_name = "ssp2-port", .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */
.platform_name = "sst-mfld-platform", .platform_name = "sst-mfld-platform",
.no_pcm = 1, .no_pcm = 1,
.codec_dai_name = "rt5640-aif1", .codec_dai_name = "rt5640-aif1", /* changed w/ quirk */
.codec_name = "i2c-10EC5640:00", /* overwritten with HID */ .codec_name = "i2c-10EC5640:00", /* overwritten with HID */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS, | SND_SOC_DAIFMT_CBS_CFS,
...@@ -335,6 +674,21 @@ static struct snd_soc_card byt_rt5640_card = { ...@@ -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_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) 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) ...@@ -343,10 +697,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
const char *i2c_name = NULL; const char *i2c_name = NULL;
int i; int i;
int dai_index; 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 */ /* register the soc card */
byt_rt5640_card.dev = &pdev->dev; byt_rt5640_card.dev = &pdev->dev;
mach = byt_rt5640_card.dev->platform_data; mach = byt_rt5640_card.dev->platform_data;
snd_soc_card_set_drvdata(&byt_rt5640_card, priv);
/* fix index of codec dai */ /* fix index of codec dai */
dai_index = MERR_DPCM_COMPR + 1; dai_index = MERR_DPCM_COMPR + 1;
...@@ -366,8 +726,57 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) ...@@ -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; 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 */ /* check quirks before creating card */
dmi_check_system(byt_rt5640_quirk_table); 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); 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 = { ...@@ -199,6 +199,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = {
static struct sst_acpi_mach broadwell_machines[] = { static struct sst_acpi_mach broadwell_machines[] = {
{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
{ "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL },
{} {}
}; };
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "../common/sst-dsp.h" #include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h" #include "../common/sst-dsp-priv.h"
#include "skl-sst-ipc.h" #include "skl-sst-ipc.h"
#include "skl-tplg-interface.h"
#define BXT_BASEFW_TIMEOUT 3000 #define BXT_BASEFW_TIMEOUT 3000
#define BXT_INIT_TIMEOUT 500 #define BXT_INIT_TIMEOUT 500
...@@ -40,11 +41,73 @@ ...@@ -40,11 +41,73 @@
#define BXT_INSTANCE_ID 0 #define BXT_INSTANCE_ID 0
#define BXT_BASE_FW_MODULE_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) static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{ {
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); 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 * 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 * 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) ...@@ -157,8 +220,6 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
return ret; return ret;
} }
#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
static int bxt_load_base_firmware(struct sst_dsp *ctx) static int bxt_load_base_firmware(struct sst_dsp *ctx)
{ {
struct firmware stripped_fw; struct firmware stripped_fw;
...@@ -175,9 +236,12 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) ...@@ -175,9 +236,12 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
if (ctx->fw == NULL) if (ctx->fw == NULL)
goto sst_load_base_firmware_failed; goto sst_load_base_firmware_failed;
ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET); /* prase uuids on first boot */
if (ret < 0) if (skl->is_first_boot) {
goto sst_load_base_firmware_failed; 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.data = ctx->fw->data;
stripped_fw.size = ctx->fw->size; stripped_fw.size = ctx->fw->size;
...@@ -230,12 +294,23 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) ...@@ -230,12 +294,23 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
int ret; int ret;
struct skl_ipc_dxstate_info dx; struct skl_ipc_dxstate_info dx;
unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
struct skl_dfw_manifest *minfo = &skl->manifest;
if (skl->fw_loaded == false) { if (skl->fw_loaded == false) {
skl->boot_complete = false; skl->boot_complete = false;
ret = bxt_load_base_firmware(ctx); ret = bxt_load_base_firmware(ctx);
if (ret < 0) if (ret < 0) {
dev_err(ctx->dev, "reload fw failed: %d\n", ret); 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; return ret;
} }
...@@ -329,7 +404,7 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) ...@@ -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); ret = skl_dsp_disable_core(ctx, core_mask);
if (ret < 0) { 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; return ret;
} }
skl->cores.state[core_id] = SKL_DSP_RESET; skl->cores.state[core_id] = SKL_DSP_RESET;
...@@ -341,6 +416,7 @@ static struct skl_dsp_fw_ops bxt_fw_ops = { ...@@ -341,6 +416,7 @@ static struct skl_dsp_fw_ops bxt_fw_ops = {
.set_state_D3 = bxt_set_dsp_D3, .set_state_D3 = bxt_set_dsp_D3,
.load_fw = bxt_load_base_firmware, .load_fw = bxt_load_base_firmware,
.get_fw_errcode = bxt_get_errorcode, .get_fw_errcode = bxt_get_errorcode,
.load_library = bxt_load_library,
}; };
static struct sst_ops skl_ops = { static struct sst_ops skl_ops = {
...@@ -397,22 +473,40 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, ...@@ -397,22 +473,40 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->cores.count = 2; skl->cores.count = 2;
skl->boot_complete = false; skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait); init_waitqueue_head(&skl->boot_wait);
skl->is_first_boot = true;
if (dsp)
*dsp = skl;
return 0;
}
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); ret = sst->fw_ops.load_fw(sst);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Load base fw failed: %x", ret); dev_err(dev, "Load base fw failed: %x\n", ret);
return ret; return ret;
} }
skl_dsp_init_core_state(sst); skl_dsp_init_core_state(sst);
if (dsp) if (ctx->manifest.lib_count > 1) {
*dsp = skl; 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; return 0;
} }
EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); EXPORT_SYMBOL_GPL(bxt_sst_init_fw);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{ {
......
...@@ -203,32 +203,35 @@ static const struct skl_dsp_ops dsp_ops[] = { ...@@ -203,32 +203,35 @@ static const struct skl_dsp_ops dsp_ops[] = {
.id = 0x9d70, .id = 0x9d70,
.loader_ops = skl_get_loader_ops, .loader_ops = skl_get_loader_ops,
.init = skl_sst_dsp_init, .init = skl_sst_dsp_init,
.init_fw = skl_sst_init_fw,
.cleanup = skl_sst_dsp_cleanup .cleanup = skl_sst_dsp_cleanup
}, },
{ {
.id = 0x9d71, .id = 0x9d71,
.loader_ops = skl_get_loader_ops, .loader_ops = skl_get_loader_ops,
.init = skl_sst_dsp_init, .init = skl_sst_dsp_init,
.init_fw = skl_sst_init_fw,
.cleanup = skl_sst_dsp_cleanup .cleanup = skl_sst_dsp_cleanup
}, },
{ {
.id = 0x5a98, .id = 0x5a98,
.loader_ops = bxt_get_loader_ops, .loader_ops = bxt_get_loader_ops,
.init = bxt_sst_dsp_init, .init = bxt_sst_dsp_init,
.init_fw = bxt_sst_init_fw,
.cleanup = bxt_sst_dsp_cleanup .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; int i;
for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) { for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) {
if (dsp_ops[i].id == pci_id) if (dsp_ops[i].id == pci_id)
return i; return &dsp_ops[i];
} }
return -EINVAL; return NULL;
} }
int skl_init_dsp(struct skl *skl) int skl_init_dsp(struct skl *skl)
...@@ -238,7 +241,8 @@ 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 hdac_bus *bus = ebus_to_hbus(ebus);
struct skl_dsp_loader_ops loader_ops; struct skl_dsp_loader_ops loader_ops;
int irq = bus->irq; int irq = bus->irq;
int ret, index; const struct skl_dsp_ops *ops;
int ret;
/* enable ppcap interrupt */ /* enable ppcap interrupt */
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
...@@ -251,18 +255,18 @@ int skl_init_dsp(struct skl *skl) ...@@ -251,18 +255,18 @@ int skl_init_dsp(struct skl *skl)
return -ENXIO; return -ENXIO;
} }
index = skl_get_dsp_ops(skl->pci->device); ops = skl_get_dsp_ops(skl->pci->device);
if (index < 0) if (!ops)
return -EINVAL; return -EIO;
loader_ops = dsp_ops[index].loader_ops(); loader_ops = ops->loader_ops();
ret = dsp_ops[index].init(bus->dev, mmio_base, irq, ret = ops->init(bus->dev, mmio_base, irq,
skl->fw_name, loader_ops, &skl->skl_sst); skl->fw_name, loader_ops,
&skl->skl_sst);
if (ret < 0) if (ret < 0)
return ret; return ret;
skl_dsp_enable_notification(skl->skl_sst, false);
dev_dbg(bus->dev, "dsp registration status=%d\n", ret); dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
return ret; return ret;
...@@ -273,16 +277,16 @@ int skl_free_dsp(struct skl *skl) ...@@ -273,16 +277,16 @@ int skl_free_dsp(struct skl *skl)
struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_ext_bus *ebus = &skl->ebus;
struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl_sst *ctx = skl->skl_sst; struct skl_sst *ctx = skl->skl_sst;
int index; const struct skl_dsp_ops *ops;
/* disable ppcap interrupt */ /* disable ppcap interrupt */
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
index = skl_get_dsp_ops(skl->pci->device); ops = skl_get_dsp_ops(skl->pci->device);
if (index < 0) if (!ops)
return -EIO; return -EIO;
dsp_ops[index].cleanup(bus->dev, ctx); ops->cleanup(bus->dev, ctx);
if (ctx->dsp->addr.lpe) if (ctx->dsp->addr.lpe)
iounmap(ctx->dsp->addr.lpe); iounmap(ctx->dsp->addr.lpe);
...@@ -296,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl) ...@@ -296,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl)
int ret; int ret;
/* if ppcap is not supported return 0 */ /* if ppcap is not supported return 0 */
if (!skl->ebus.ppcap) if (!skl->ebus.bus.ppcap)
return 0; return 0;
ret = skl_dsp_sleep(ctx->dsp); ret = skl_dsp_sleep(ctx->dsp);
...@@ -316,13 +320,17 @@ int skl_resume_dsp(struct skl *skl) ...@@ -316,13 +320,17 @@ int skl_resume_dsp(struct skl *skl)
int ret; int ret;
/* if ppcap is not supported return 0 */ /* if ppcap is not supported return 0 */
if (!skl->ebus.ppcap) if (!skl->ebus.bus.ppcap)
return 0; return 0;
/* enable ppcap interrupt */ /* enable ppcap interrupt */
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
snd_hdac_ext_bus_ppcap_int_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); ret = skl_dsp_wake(ctx->dsp);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -672,6 +680,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, ...@@ -672,6 +680,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx,
return param_size; return param_size;
case SKL_MODULE_TYPE_BASE_OUTFMT: case SKL_MODULE_TYPE_BASE_OUTFMT:
case SKL_MODULE_TYPE_KPB:
return sizeof(struct skl_base_outfmt_cfg); return sizeof(struct skl_base_outfmt_cfg);
default: default:
...@@ -725,6 +734,7 @@ static int skl_set_module_format(struct skl_sst *ctx, ...@@ -725,6 +734,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
break; break;
case SKL_MODULE_TYPE_BASE_OUTFMT: case SKL_MODULE_TYPE_BASE_OUTFMT:
case SKL_MODULE_TYPE_KPB:
skl_set_base_outfmt_format(ctx, module_config, *param_data); skl_set_base_outfmt_format(ctx, module_config, *param_data);
break; break;
...@@ -779,6 +789,7 @@ static int skl_alloc_queue(struct skl_module_pin *mpin, ...@@ -779,6 +789,7 @@ static int skl_alloc_queue(struct skl_module_pin *mpin,
mpin[i].in_use = true; mpin[i].in_use = true;
mpin[i].id.module_id = id.module_id; mpin[i].id.module_id = id.module_id;
mpin[i].id.instance_id = id.instance_id; mpin[i].id.instance_id = id.instance_id;
mpin[i].id.pvt_id = id.pvt_id;
mpin[i].tgt_mcfg = tgt_cfg; mpin[i].tgt_mcfg = tgt_cfg;
return i; return i;
} }
...@@ -802,6 +813,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index) ...@@ -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].in_use = false;
mpin[q_index].id.module_id = 0; mpin[q_index].id.module_id = 0;
mpin[q_index].id.instance_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].pin_state = SKL_PIN_UNBIND;
mpin[q_index].tgt_mcfg = NULL; mpin[q_index].tgt_mcfg = NULL;
...@@ -842,7 +854,7 @@ int skl_init_module(struct skl_sst *ctx, ...@@ -842,7 +854,7 @@ int skl_init_module(struct skl_sst *ctx,
struct skl_ipc_init_instance_msg msg; struct skl_ipc_init_instance_msg msg;
dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, 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) { if (mconfig->pipe->state != SKL_PIPE_CREATED) {
dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n", 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, ...@@ -858,10 +870,11 @@ int skl_init_module(struct skl_sst *ctx,
} }
msg.module_id = mconfig->id.module_id; 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.ppl_instance_id = mconfig->pipe->ppl_id;
msg.param_data_size = module_config_size; msg.param_data_size = module_config_size;
msg.core_id = mconfig->core_id; msg.core_id = mconfig->core_id;
msg.domain = mconfig->domain;
ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data); ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data);
if (ret < 0) { if (ret < 0) {
...@@ -878,9 +891,9 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg ...@@ -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) *src_module, struct skl_module_cfg *dst_module)
{ {
dev_dbg(ctx->dev, "%s: src module_id = %d src_instance=%d\n", 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__, 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", dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n",
src_module->m_state, dst_module->m_state); src_module->m_state, dst_module->m_state);
...@@ -927,9 +940,9 @@ int skl_unbind_modules(struct skl_sst *ctx, ...@@ -927,9 +940,9 @@ int skl_unbind_modules(struct skl_sst *ctx,
return 0; return 0;
msg.module_id = src_mcfg->id.module_id; 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_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; msg.bind = false;
ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
...@@ -988,9 +1001,9 @@ int skl_bind_modules(struct skl_sst *ctx, ...@@ -988,9 +1001,9 @@ int skl_bind_modules(struct skl_sst *ctx,
msg.src_queue, msg.dst_queue); msg.src_queue, msg.dst_queue);
msg.module_id = src_mcfg->id.module_id; 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_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; msg.bind = true;
ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); 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, ...@@ -1168,7 +1181,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
struct skl_ipc_large_config_msg msg; struct skl_ipc_large_config_msg msg;
msg.module_id = mcfg->id.module_id; 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.param_data_size = size;
msg.large_param_id = param_id; msg.large_param_id = param_id;
...@@ -1181,7 +1194,7 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, ...@@ -1181,7 +1194,7 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
struct skl_ipc_large_config_msg msg; struct skl_ipc_large_config_msg msg;
msg.module_id = mcfg->id.module_id; 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.param_data_size = size;
msg.large_param_id = param_id; msg.large_param_id = param_id;
......
...@@ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, ...@@ -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) 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; return HDAC_EXT_STREAM_TYPE_HOST;
else else
return HDAC_EXT_STREAM_TYPE_COUPLED; return HDAC_EXT_STREAM_TYPE_COUPLED;
...@@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream, ...@@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
int format_val = 0; int format_val = 0;
if (ebus->ppcap) { if ((ebus_to_hbus(ebus))->ppcap) {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
format_val = snd_hdac_calc_stream_format(runtime->rate, format_val = snd_hdac_calc_stream_format(runtime->rate,
...@@ -648,7 +648,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { ...@@ -648,7 +648,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.channels_min = HDA_MONO, .channels_min = HDA_MONO,
.channels_max = HDA_STEREO, .channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, .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 = { .capture = {
.stream_name = "System Capture", .stream_name = "System Capture",
...@@ -1020,7 +1021,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, ...@@ -1020,7 +1021,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
{ {
struct hdac_ext_bus *ebus = get_bus_ctx(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 skl_coupled_trigger(substream, cmd);
return 0; return 0;
...@@ -1138,20 +1139,67 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) ...@@ -1138,20 +1139,67 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval; 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) static int skl_platform_soc_probe(struct snd_soc_platform *platform)
{ {
struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev); struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
struct skl *skl = ebus_to_skl(ebus); struct skl *skl = ebus_to_skl(ebus);
const struct skl_dsp_ops *ops;
int ret; int ret;
if (ebus->ppcap) { pm_runtime_get_sync(platform->dev);
if ((ebus_to_hbus(ebus))->ppcap) {
ret = skl_tplg_init(platform, ebus); ret = skl_tplg_init(platform, ebus);
if (ret < 0) { if (ret < 0) {
dev_err(platform->dev, "Failed to init topology!\n"); dev_err(platform->dev, "Failed to init topology!\n");
return ret; return ret;
} }
skl->platform = platform; 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; return 0;
} }
......
...@@ -341,14 +341,14 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ...@@ -341,14 +341,14 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
&ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize);
if (ret < 0) { 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; return ret;
} }
/* Setup Code loader BDL */ /* Setup Code loader BDL */
ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
&ctx->cl_dev.dmab_bdl, PAGE_SIZE); &ctx->cl_dev.dmab_bdl, PAGE_SIZE);
if (ret < 0) { 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); ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
return ret; return ret;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <sound/memalloc.h> #include <sound/memalloc.h>
#include "skl-sst-cldma.h" #include "skl-sst-cldma.h"
#include "skl-tplg-interface.h" #include "skl-tplg-interface.h"
#include "skl-topology.h"
struct sst_dsp; struct sst_dsp;
struct skl_sst; struct skl_sst;
...@@ -133,6 +134,8 @@ enum skl_dsp_states { ...@@ -133,6 +134,8 @@ enum skl_dsp_states {
struct skl_dsp_fw_ops { struct skl_dsp_fw_ops {
int (*load_fw)(struct sst_dsp *ctx); int (*load_fw)(struct sst_dsp *ctx);
/* FW module parser/loader */ /* FW module parser/loader */
int (*load_library)(struct sst_dsp *ctx,
struct skl_dfw_manifest *minfo);
int (*parse_fw)(struct sst_dsp *ctx); int (*parse_fw)(struct sst_dsp *ctx);
int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
int (*set_state_D3)(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, ...@@ -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, 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, const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
struct skl_sst **dsp); 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 skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
void bxt_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, int snd_skl_get_module_info(struct skl_sst *ctx,
struct skl_dfw_module *dfw_config); struct skl_module_cfg *mconfig);
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);
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); void skl_freeup_uuid_list(struct skl_sst *ctx);
int skl_dsp_strip_extended_manifest(struct firmware *fw); int skl_dsp_strip_extended_manifest(struct firmware *fw);
......
...@@ -114,6 +114,11 @@ ...@@ -114,6 +114,11 @@
#define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \ #define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \
<< IPC_CORE_ID_SHIFT) << 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 */ /* Bind/Unbind message extension register */
#define IPC_DST_MOD_ID_SHIFT 0 #define IPC_DST_MOD_ID_SHIFT 0
#define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \ #define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \
...@@ -190,6 +195,7 @@ enum skl_ipc_glb_type { ...@@ -190,6 +195,7 @@ enum skl_ipc_glb_type {
IPC_GLB_GET_PPL_CONTEXT_SIZE = 21, IPC_GLB_GET_PPL_CONTEXT_SIZE = 21,
IPC_GLB_SAVE_PPL = 22, IPC_GLB_SAVE_PPL = 22,
IPC_GLB_RESTORE_PPL = 23, IPC_GLB_RESTORE_PPL = 23,
IPC_GLB_LOAD_LIBRARY = 24,
IPC_GLB_NOTIFY = 26, IPC_GLB_NOTIFY = 26,
IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ 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, ...@@ -338,7 +344,7 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
break; break;
default: default:
dev_err(ipc->dev, "ipc: Unhandled error msg=%x", dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n",
header.primary); header.primary);
break; break;
} }
...@@ -379,13 +385,13 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, ...@@ -379,13 +385,13 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
break; break;
default: 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; msg->errno = -EINVAL;
break; break;
} }
if (reply != IPC_GLB_REPLY_SUCCESS) { 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", dev_err(ipc->dev, "FW Error Code: %u\n",
ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp));
} }
...@@ -434,9 +440,9 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) ...@@ -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); hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE);
header.primary = hipct; header.primary = hipct;
header.extension = hipcte; 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); 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); header.extension);
if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) { if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
...@@ -704,6 +710,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc, ...@@ -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_CORE_ID(msg->core_id);
header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id); header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id);
header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size); 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__, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
header.primary, header.extension); header.primary, header.extension);
...@@ -742,7 +749,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, ...@@ -742,7 +749,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
header.extension); header.extension);
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
if (ret < 0) { if (ret < 0) {
dev_err(ipc->dev, "ipc: bind/unbind faileden"); dev_err(ipc->dev, "ipc: bind/unbind failed\n");
return ret; return ret;
} }
...@@ -902,3 +909,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, ...@@ -902,3 +909,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); 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 { ...@@ -66,7 +66,7 @@ struct skl_sst {
/* callback for miscbdge */ /* callback for miscbdge */
void (*enable_miscbdcge)(struct device *dev, bool enable); void (*enable_miscbdcge)(struct device *dev, bool enable);
/*Is CGCTL.MISCBDCGE disabled*/ /* Is CGCTL.MISCBDCGE disabled */
bool miscbdcg_disabled; bool miscbdcg_disabled;
/* Populate module information */ /* Populate module information */
...@@ -75,8 +75,14 @@ struct skl_sst { ...@@ -75,8 +75,14 @@ struct skl_sst {
/* Is firmware loaded */ /* Is firmware loaded */
bool fw_loaded; bool fw_loaded;
/* first boot ? */
bool is_first_boot;
/* multi-core */ /* multi-core */
struct skl_dsp_cores cores; struct skl_dsp_cores cores;
/* tplg manifest */
struct skl_dfw_manifest manifest;
}; };
struct skl_ipc_init_instance_msg { struct skl_ipc_init_instance_msg {
...@@ -85,6 +91,7 @@ struct skl_ipc_init_instance_msg { ...@@ -85,6 +91,7 @@ struct skl_ipc_init_instance_msg {
u16 param_data_size; u16 param_data_size;
u8 ppl_instance_id; u8 ppl_instance_id;
u8 core_id; u8 core_id;
u8 domain;
}; };
struct skl_ipc_bind_unbind_msg { struct skl_ipc_bind_unbind_msg {
...@@ -145,6 +152,9 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, ...@@ -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, int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
struct skl_ipc_large_config_msg *msg, u32 *param); 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_int_enable(struct sst_dsp *dsp);
void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_enable(struct sst_dsp *ctx);
void skl_ipc_op_int_disable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx);
......
...@@ -28,11 +28,6 @@ ...@@ -28,11 +28,6 @@
/* FW Extended Manifest Header id = $AE1 */ /* FW Extended Manifest Header id = $AE1 */
#define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124
struct skl_dfw_module_mod {
char name[100];
struct skl_dfw_module skl_dfw_mod;
};
struct UUID { struct UUID {
u8 id[16]; u8 id[16];
}; };
...@@ -99,10 +94,15 @@ struct adsp_fw_hdr { ...@@ -99,10 +94,15 @@ struct adsp_fw_hdr {
u32 load_offset; u32 load_offset;
} __packed; } __packed;
#define MAX_INSTANCE_BUFF 2
struct uuid_module { struct uuid_module {
uuid_le uuid; uuid_le uuid;
int id; int id;
int is_loadable; int is_loadable;
int max_instance;
u64 pvt_id[MAX_INSTANCE_BUFF];
int *instance_id;
struct list_head list; struct list_head list;
}; };
...@@ -115,13 +115,13 @@ struct skl_ext_manifest_hdr { ...@@ -115,13 +115,13 @@ struct skl_ext_manifest_hdr {
u32 entries; u32 entries;
}; };
int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, int snd_skl_get_module_info(struct skl_sst *ctx,
struct skl_dfw_module *dfw_config) struct skl_module_cfg *mconfig)
{ {
struct uuid_module *module; struct uuid_module *module;
uuid_le *uuid_mod; uuid_le *uuid_mod;
uuid_mod = (uuid_le *)uuid; uuid_mod = (uuid_le *)mconfig->guid;
if (list_empty(&ctx->uuid_list)) { if (list_empty(&ctx->uuid_list)) {
dev_err(ctx->dev, "Module list is empty\n"); 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, ...@@ -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) { list_for_each_entry(module, &ctx->uuid_list, list) {
if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
dfw_config->module_id = module->id; mconfig->id.module_id = module->id;
dfw_config->is_loadable = module->is_loadable; mconfig->is_loadable = module->is_loadable;
return 0; return 0;
} }
...@@ -141,15 +141,154 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid, ...@@ -141,15 +141,154 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
} }
EXPORT_SYMBOL_GPL(snd_skl_get_module_info); 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 * Parse the firmware binary to get the UUID, module id
* and loadable flags * 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_fw_hdr *adsp_hdr;
struct adsp_module_entry *mod_entry; struct adsp_module_entry *mod_entry;
int i, num_entry; int i, num_entry, size;
uuid_le *uuid_bin; uuid_le *uuid_bin;
const char *buf; const char *buf;
struct skl_sst *skl = ctx->thread_context; struct skl_sst *skl = ctx->thread_context;
...@@ -158,8 +297,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) ...@@ -158,8 +297,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
unsigned int safe_file; unsigned int safe_file;
/* Get the FW pointer to derive ADSP header */ /* Get the FW pointer to derive ADSP header */
stripped_fw.data = ctx->fw->data; stripped_fw.data = fw->data;
stripped_fw.size = ctx->fw->size; stripped_fw.size = fw->size;
skl_dsp_strip_extended_manifest(&stripped_fw); skl_dsp_strip_extended_manifest(&stripped_fw);
...@@ -210,8 +349,15 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset) ...@@ -210,8 +349,15 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
uuid_bin = (uuid_le *)mod_entry->uuid.id; uuid_bin = (uuid_le *)mod_entry->uuid.id;
memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); 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->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); list_add_tail(&module->list, &skl->uuid_list);
......
...@@ -88,13 +88,15 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) ...@@ -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); /* prase uuids on first boot */
if (ret < 0) { if (skl->is_first_boot) {
dev_err(ctx->dev, ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0);
"UUID parsing err: %d\n", ret); if (ret < 0) {
release_firmware(ctx->fw); dev_err(ctx->dev, "UUID parsing err: %d\n", ret);
skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); release_firmware(ctx->fw);
return ret; skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
return ret;
}
} }
/* check for extended manifest */ /* check for extended manifest */
...@@ -105,13 +107,13 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) ...@@ -105,13 +107,13 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
ret = skl_dsp_boot(ctx); ret = skl_dsp_boot(ctx);
if (ret < 0) { 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; goto skl_load_base_firmware_failed;
} }
ret = skl_cldma_prepare(ctx); ret = skl_cldma_prepare(ctx);
if (ret < 0) { 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; goto skl_load_base_firmware_failed;
} }
...@@ -484,25 +486,32 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, ...@@ -484,25 +486,32 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
return ret; return ret;
skl->cores.count = 2; skl->cores.count = 2;
skl->is_first_boot = true;
if (dsp)
*dsp = 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); ret = sst->fw_ops.load_fw(sst);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Load base fw failed : %d", ret); dev_err(dev, "Load base fw failed : %d\n", ret);
goto cleanup; return ret;
} }
skl_dsp_init_core_state(sst); skl_dsp_init_core_state(sst);
ctx->is_first_boot = false;
if (dsp) return 0;
*dsp = skl;
return ret;
cleanup:
skl_sst_dsp_cleanup(dev, skl);
return ret;
} }
EXPORT_SYMBOL_GPL(skl_sst_dsp_init); EXPORT_SYMBOL_GPL(skl_sst_init_fw);
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{ {
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/soc-topology.h> #include <sound/soc-topology.h>
#include <uapi/sound/snd_sst_tokens.h>
#include "skl-sst-dsp.h" #include "skl-sst-dsp.h"
#include "skl-sst-ipc.h" #include "skl-sst-ipc.h"
#include "skl-topology.h" #include "skl-topology.h"
...@@ -32,6 +33,8 @@ ...@@ -32,6 +33,8 @@
#define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_CH_FIXUP_MASK (1 << 0)
#define SKL_RATE_FIXUP_MASK (1 << 1) #define SKL_RATE_FIXUP_MASK (1 << 1)
#define SKL_FMT_FIXUP_MASK (1 << 2) #define SKL_FMT_FIXUP_MASK (1 << 2)
#define SKL_IN_DIR_BIT_MASK BIT(0)
#define SKL_PIN_COUNT_MASK GENMASK(7, 4)
/* /*
* SKL DSP driver modelling uses only few DAPM widgets so for rest we will * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
...@@ -473,6 +476,14 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) ...@@ -473,6 +476,14 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
w = w_module->w; w = w_module->w;
mconfig = w->priv; mconfig = w->priv;
/* check if module ids are populated */
if (mconfig->id.module_id < 0) {
dev_err(skl->skl_sst->dev,
"module %pUL id not populated\n",
(uuid_le *)mconfig->guid);
return -EIO;
}
/* check resource available */ /* check resource available */
if (!skl_is_pipe_mcps_avail(skl, mconfig)) if (!skl_is_pipe_mcps_avail(skl, mconfig))
return -ENOMEM; return -ENOMEM;
...@@ -494,12 +505,15 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) ...@@ -494,12 +505,15 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
* FE/BE params * FE/BE params
*/ */
skl_tplg_update_module_params(w, ctx); skl_tplg_update_module_params(w, ctx);
mconfig->id.pvt_id = skl_get_pvt_id(ctx, mconfig);
if (mconfig->id.pvt_id < 0)
return ret;
skl_tplg_set_module_init_data(w); skl_tplg_set_module_init_data(w);
ret = skl_init_module(ctx, mconfig); ret = skl_init_module(ctx, mconfig);
if (ret < 0) if (ret < 0) {
skl_put_pvt_id(ctx, mconfig);
return ret; return ret;
}
skl_tplg_alloc_pipe_mcps(skl, mconfig); skl_tplg_alloc_pipe_mcps(skl, mconfig);
ret = skl_tplg_set_module_params(w, ctx); ret = skl_tplg_set_module_params(w, ctx);
if (ret < 0) if (ret < 0)
...@@ -512,6 +526,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) ...@@ -512,6 +526,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
struct skl_pipe *pipe) struct skl_pipe *pipe)
{ {
int ret;
struct skl_pipe_module *w_module = NULL; struct skl_pipe_module *w_module = NULL;
struct skl_module_cfg *mconfig = NULL; struct skl_module_cfg *mconfig = NULL;
...@@ -519,9 +534,13 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, ...@@ -519,9 +534,13 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
mconfig = w_module->w->priv; mconfig = w_module->w->priv;
if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod && if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod &&
mconfig->m_state > SKL_MODULE_UNINIT) mconfig->m_state > SKL_MODULE_UNINIT) {
return ctx->dsp->fw_ops.unload_mod(ctx->dsp, ret = ctx->dsp->fw_ops.unload_mod(ctx->dsp,
mconfig->id.module_id); mconfig->id.module_id);
if (ret < 0)
return -EIO;
}
skl_put_pvt_id(ctx, mconfig);
} }
/* no modules to unload in this path, so return */ /* no modules to unload in this path, so return */
...@@ -588,6 +607,26 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, ...@@ -588,6 +607,26 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
return 0; return 0;
} }
static int skl_fill_sink_instance_id(struct skl_sst *ctx,
struct skl_algo_data *alg_data)
{
struct skl_kpb_params *params = (struct skl_kpb_params *)alg_data->params;
struct skl_mod_inst_map *inst;
int i, pvt_id;
inst = params->map;
for (i = 0; i < params->num_modules; i++) {
pvt_id = skl_get_pvt_instance_id_map(ctx,
inst->mod_id, inst->inst_id);
if (pvt_id < 0)
return -EINVAL;
inst->inst_id = pvt_id;
inst++;
}
return 0;
}
/* /*
* Some modules require params to be set after the module is bound to * Some modules require params to be set after the module is bound to
* all pins connected. * all pins connected.
...@@ -636,6 +675,8 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, ...@@ -636,6 +675,8 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
bc = (struct skl_algo_data *)sb->dobj.private; bc = (struct skl_algo_data *)sb->dobj.private;
if (bc->set_params == SKL_PARAM_BIND) { if (bc->set_params == SKL_PARAM_BIND) {
if (mconfig->m_type == SKL_MODULE_TYPE_KPB)
skl_fill_sink_instance_id(ctx, bc);
ret = skl_set_module_params(ctx, ret = skl_set_module_params(ctx,
(u32 *)bc->params, bc->max, (u32 *)bc->params, bc->max,
bc->param_id, mconfig); bc->param_id, mconfig);
...@@ -1460,85 +1501,570 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { ...@@ -1460,85 +1501,570 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
skl_tplg_tlv_control_set}, skl_tplg_tlv_control_set},
}; };
/* static int skl_tplg_fill_pipe_tkn(struct device *dev,
* The topology binary passes the pin info for a module so initialize the pin struct skl_pipe *pipe, u32 tkn,
* info passed into module instance u32 tkn_val)
*/
static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
struct skl_module_pin *m_pin,
bool is_dynamic, int max_pin)
{ {
int i;
for (i = 0; i < max_pin; i++) { switch (tkn) {
m_pin[i].id.module_id = dfw_pin[i].module_id; case SKL_TKN_U32_PIPE_CONN_TYPE:
m_pin[i].id.instance_id = dfw_pin[i].instance_id; pipe->conn_type = tkn_val;
m_pin[i].in_use = false; break;
m_pin[i].is_dynamic = is_dynamic;
m_pin[i].pin_state = SKL_PIN_UNBIND; case SKL_TKN_U32_PIPE_PRIORITY:
pipe->pipe_priority = tkn_val;
break;
case SKL_TKN_U32_PIPE_MEM_PGS:
pipe->memory_pages = tkn_val;
break;
default:
dev_err(dev, "Token not handled %d\n", tkn);
return -EINVAL;
} }
return 0;
} }
/* /*
* Add pipeline from topology binary into driver pipeline list * Add pipeline by parsing the relevant tokens
* * Return an existing pipe if the pipe already exists.
* If already added we return that instance
* Otherwise we create a new instance and add into driver list
*/ */
static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, static int skl_tplg_add_pipe(struct device *dev,
struct skl *skl, struct skl_dfw_pipe *dfw_pipe) struct skl_module_cfg *mconfig, struct skl *skl,
struct snd_soc_tplg_vendor_value_elem *tkn_elem)
{ {
struct skl_pipeline *ppl; struct skl_pipeline *ppl;
struct skl_pipe *pipe; struct skl_pipe *pipe;
struct skl_pipe_params *params; struct skl_pipe_params *params;
list_for_each_entry(ppl, &skl->ppl_list, node) { list_for_each_entry(ppl, &skl->ppl_list, node) {
if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) if (ppl->pipe->ppl_id == tkn_elem->value) {
return ppl->pipe; mconfig->pipe = ppl->pipe;
return EEXIST;
}
} }
ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
if (!ppl) if (!ppl)
return NULL; return -ENOMEM;
pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
if (!pipe) if (!pipe)
return NULL; return -ENOMEM;
params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
if (!params) if (!params)
return NULL; return -ENOMEM;
pipe->ppl_id = dfw_pipe->pipe_id;
pipe->memory_pages = dfw_pipe->memory_pages;
pipe->pipe_priority = dfw_pipe->pipe_priority;
pipe->conn_type = dfw_pipe->conn_type;
pipe->state = SKL_PIPE_INVALID;
pipe->p_params = params; pipe->p_params = params;
pipe->ppl_id = tkn_elem->value;
INIT_LIST_HEAD(&pipe->w_list); INIT_LIST_HEAD(&pipe->w_list);
ppl->pipe = pipe; ppl->pipe = pipe;
list_add(&ppl->node, &skl->ppl_list); list_add(&ppl->node, &skl->ppl_list);
return ppl->pipe; mconfig->pipe = pipe;
mconfig->pipe->state = SKL_PIPE_INVALID;
return 0;
}
static int skl_tplg_fill_pin(struct device *dev, u32 tkn,
struct skl_module_pin *m_pin,
int pin_index, u32 value)
{
switch (tkn) {
case SKL_TKN_U32_PIN_MOD_ID:
m_pin[pin_index].id.module_id = value;
break;
case SKL_TKN_U32_PIN_INST_ID:
m_pin[pin_index].id.instance_id = value;
break;
default:
dev_err(dev, "%d Not a pin token\n", value);
return -EINVAL;
}
return 0;
}
/*
* Parse for pin config specific tokens to fill up the
* module private data
*/
static int skl_tplg_fill_pins_info(struct device *dev,
struct skl_module_cfg *mconfig,
struct snd_soc_tplg_vendor_value_elem *tkn_elem,
int dir, int pin_count)
{
int ret;
struct skl_module_pin *m_pin;
switch (dir) {
case SKL_DIR_IN:
m_pin = mconfig->m_in_pin;
break;
case SKL_DIR_OUT:
m_pin = mconfig->m_out_pin;
break;
default:
dev_err(dev, "Invalid direction value\n");
return -EINVAL;
}
ret = skl_tplg_fill_pin(dev, tkn_elem->token,
m_pin, pin_count, tkn_elem->value);
if (ret < 0)
return ret;
m_pin[pin_count].in_use = false;
m_pin[pin_count].pin_state = SKL_PIN_UNBIND;
return 0;
}
/*
* Fill up input/output module config format based
* on the direction
*/
static int skl_tplg_fill_fmt(struct device *dev,
struct skl_module_cfg *mconfig, u32 tkn,
u32 value, u32 dir, u32 pin_count)
{
struct skl_module_fmt *dst_fmt;
switch (dir) {
case SKL_DIR_IN:
dst_fmt = mconfig->in_fmt;
dst_fmt += pin_count;
break;
case SKL_DIR_OUT:
dst_fmt = mconfig->out_fmt;
dst_fmt += pin_count;
break;
default:
dev_err(dev, "Invalid direction value\n");
return -EINVAL;
}
switch (tkn) {
case SKL_TKN_U32_FMT_CH:
dst_fmt->channels = value;
break;
case SKL_TKN_U32_FMT_FREQ:
dst_fmt->s_freq = value;
break;
case SKL_TKN_U32_FMT_BIT_DEPTH:
dst_fmt->bit_depth = value;
break;
case SKL_TKN_U32_FMT_SAMPLE_SIZE:
dst_fmt->valid_bit_depth = value;
break;
case SKL_TKN_U32_FMT_CH_CONFIG:
dst_fmt->ch_cfg = value;
break;
case SKL_TKN_U32_FMT_INTERLEAVE:
dst_fmt->interleaving_style = value;
break;
case SKL_TKN_U32_FMT_SAMPLE_TYPE:
dst_fmt->sample_type = value;
break;
case SKL_TKN_U32_FMT_CH_MAP:
dst_fmt->ch_map = value;
break;
default:
dev_err(dev, "Invalid token %d\n", tkn);
return -EINVAL;
}
return 0;
} }
static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt, static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig,
struct skl_dfw_module_fmt *src_fmt, struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn)
int pins) {
if (uuid_tkn->token == SKL_TKN_UUID)
memcpy(&mconfig->guid, &uuid_tkn->uuid, 16);
else {
dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token);
return -EINVAL;
}
return 0;
}
static void skl_tplg_fill_pin_dynamic_val(
struct skl_module_pin *mpin, u32 pin_count, u32 value)
{ {
int i; int i;
for (i = 0; i < pins; i++) { for (i = 0; i < pin_count; i++)
dst_fmt[i].channels = src_fmt[i].channels; mpin[i].is_dynamic = value;
dst_fmt[i].s_freq = src_fmt[i].freq; }
dst_fmt[i].bit_depth = src_fmt[i].bit_depth;
dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth; /*
dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg; * Parse tokens to fill up the module private data
dst_fmt[i].ch_map = src_fmt[i].ch_map; */
dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style; static int skl_tplg_get_token(struct device *dev,
dst_fmt[i].sample_type = src_fmt[i].sample_type; struct snd_soc_tplg_vendor_value_elem *tkn_elem,
struct skl *skl, struct skl_module_cfg *mconfig)
{
int tkn_count = 0;
int ret;
static int is_pipe_exists;
static int pin_index, dir;
if (tkn_elem->token > SKL_TKN_MAX)
return -EINVAL;
switch (tkn_elem->token) {
case SKL_TKN_U8_IN_QUEUE_COUNT:
mconfig->max_in_queue = tkn_elem->value;
mconfig->m_in_pin = devm_kzalloc(dev, mconfig->max_in_queue *
sizeof(*mconfig->m_in_pin),
GFP_KERNEL);
if (!mconfig->m_in_pin)
return -ENOMEM;
break;
case SKL_TKN_U8_OUT_QUEUE_COUNT:
mconfig->max_out_queue = tkn_elem->value;
mconfig->m_out_pin = devm_kzalloc(dev, mconfig->max_out_queue *
sizeof(*mconfig->m_out_pin),
GFP_KERNEL);
if (!mconfig->m_out_pin)
return -ENOMEM;
break;
case SKL_TKN_U8_DYN_IN_PIN:
if (!mconfig->m_in_pin)
return -ENOMEM;
skl_tplg_fill_pin_dynamic_val(mconfig->m_in_pin,
mconfig->max_in_queue, tkn_elem->value);
break;
case SKL_TKN_U8_DYN_OUT_PIN:
if (!mconfig->m_out_pin)
return -ENOMEM;
skl_tplg_fill_pin_dynamic_val(mconfig->m_out_pin,
mconfig->max_out_queue, tkn_elem->value);
break;
case SKL_TKN_U8_TIME_SLOT:
mconfig->time_slot = tkn_elem->value;
break;
case SKL_TKN_U8_CORE_ID:
mconfig->core_id = tkn_elem->value;
case SKL_TKN_U8_MOD_TYPE:
mconfig->m_type = tkn_elem->value;
break;
case SKL_TKN_U8_DEV_TYPE:
mconfig->dev_type = tkn_elem->value;
break;
case SKL_TKN_U8_HW_CONN_TYPE:
mconfig->hw_conn_type = tkn_elem->value;
break;
case SKL_TKN_U16_MOD_INST_ID:
mconfig->id.instance_id =
tkn_elem->value;
break;
case SKL_TKN_U32_MEM_PAGES:
mconfig->mem_pages = tkn_elem->value;
break;
case SKL_TKN_U32_MAX_MCPS:
mconfig->mcps = tkn_elem->value;
break;
case SKL_TKN_U32_OBS:
mconfig->obs = tkn_elem->value;
break;
case SKL_TKN_U32_IBS:
mconfig->ibs = tkn_elem->value;
break;
case SKL_TKN_U32_VBUS_ID:
mconfig->vbus_id = tkn_elem->value;
break;
case SKL_TKN_U32_PARAMS_FIXUP:
mconfig->params_fixup = tkn_elem->value;
break;
case SKL_TKN_U32_CONVERTER:
mconfig->converter = tkn_elem->value;
break;
case SKL_TKN_U32_PIPE_ID:
ret = skl_tplg_add_pipe(dev,
mconfig, skl, tkn_elem);
if (ret < 0)
return is_pipe_exists;
if (ret == EEXIST)
is_pipe_exists = 1;
break;
case SKL_TKN_U32_PIPE_CONN_TYPE:
case SKL_TKN_U32_PIPE_PRIORITY:
case SKL_TKN_U32_PIPE_MEM_PGS:
if (is_pipe_exists) {
ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe,
tkn_elem->token, tkn_elem->value);
if (ret < 0)
return ret;
}
break;
/*
* SKL_TKN_U32_DIR_PIN_COUNT token has the value for both
* direction and the pin count. The first four bits represent
* direction and next four the pin count.
*/
case SKL_TKN_U32_DIR_PIN_COUNT:
dir = tkn_elem->value & SKL_IN_DIR_BIT_MASK;
pin_index = (tkn_elem->value &
SKL_PIN_COUNT_MASK) >> 4;
break;
case SKL_TKN_U32_FMT_CH:
case SKL_TKN_U32_FMT_FREQ:
case SKL_TKN_U32_FMT_BIT_DEPTH:
case SKL_TKN_U32_FMT_SAMPLE_SIZE:
case SKL_TKN_U32_FMT_CH_CONFIG:
case SKL_TKN_U32_FMT_INTERLEAVE:
case SKL_TKN_U32_FMT_SAMPLE_TYPE:
case SKL_TKN_U32_FMT_CH_MAP:
ret = skl_tplg_fill_fmt(dev, mconfig, tkn_elem->token,
tkn_elem->value, dir, pin_index);
if (ret < 0)
return ret;
break;
case SKL_TKN_U32_PIN_MOD_ID:
case SKL_TKN_U32_PIN_INST_ID:
ret = skl_tplg_fill_pins_info(dev,
mconfig, tkn_elem, dir,
pin_index);
if (ret < 0)
return ret;
break;
case SKL_TKN_U32_CAPS_SIZE:
mconfig->formats_config.caps_size =
tkn_elem->value;
break;
case SKL_TKN_U32_PROC_DOMAIN:
mconfig->domain =
tkn_elem->value;
break;
case SKL_TKN_U8_IN_PIN_TYPE:
case SKL_TKN_U8_OUT_PIN_TYPE:
case SKL_TKN_U8_CONN_TYPE:
break;
default:
dev_err(dev, "Token %d not handled\n",
tkn_elem->token);
return -EINVAL;
}
tkn_count++;
return tkn_count;
}
/*
* Parse the vendor array for specific tokens to construct
* module private data
*/
static int skl_tplg_get_tokens(struct device *dev,
char *pvt_data, struct skl *skl,
struct skl_module_cfg *mconfig, int block_size)
{
struct snd_soc_tplg_vendor_array *array;
struct snd_soc_tplg_vendor_value_elem *tkn_elem;
int tkn_count = 0, ret;
int off = 0, tuple_size = 0;
if (block_size <= 0)
return -EINVAL;
while (tuple_size < block_size) {
array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off);
off += array->size;
switch (array->type) {
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
dev_warn(dev, "no string tokens expected for skl tplg\n");
continue;
case SND_SOC_TPLG_TUPLE_TYPE_UUID:
ret = skl_tplg_get_uuid(dev, mconfig, array->uuid);
if (ret < 0)
return ret;
tuple_size += sizeof(*array->uuid);
continue;
default:
tkn_elem = array->value;
tkn_count = 0;
break;
}
while (tkn_count <= (array->num_elems - 1)) {
ret = skl_tplg_get_token(dev, tkn_elem,
skl, mconfig);
if (ret < 0)
return ret;
tkn_count = tkn_count + ret;
tkn_elem++;
}
tuple_size += tkn_count * sizeof(*tkn_elem);
}
return 0;
}
/*
* Every data block is preceded by a descriptor to read the number
* of data blocks, they type of the block and it's size
*/
static int skl_tplg_get_desc_blocks(struct device *dev,
struct snd_soc_tplg_vendor_array *array)
{
struct snd_soc_tplg_vendor_value_elem *tkn_elem;
tkn_elem = array->value;
switch (tkn_elem->token) {
case SKL_TKN_U8_NUM_BLOCKS:
case SKL_TKN_U8_BLOCK_TYPE:
case SKL_TKN_U16_BLOCK_SIZE:
return tkn_elem->value;
default:
dev_err(dev, "Invalid descriptor token %d\n", tkn_elem->token);
break;
}
return -EINVAL;
}
/*
* Parse the private data for the token and corresponding value.
* The private data can have multiple data blocks. So, a data block
* is preceded by a descriptor for number of blocks and a descriptor
* for the type and size of the suceeding data block.
*/
static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w,
struct skl *skl, struct device *dev,
struct skl_module_cfg *mconfig)
{
struct snd_soc_tplg_vendor_array *array;
int num_blocks, block_size = 0, block_type, off = 0;
char *data;
int ret;
/* Read the NUM_DATA_BLOCKS descriptor */
array = (struct snd_soc_tplg_vendor_array *)tplg_w->priv.data;
ret = skl_tplg_get_desc_blocks(dev, array);
if (ret < 0)
return ret;
num_blocks = ret;
off += array->size;
array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data + off);
/* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */
while (num_blocks > 0) {
ret = skl_tplg_get_desc_blocks(dev, array);
if (ret < 0)
return ret;
block_type = ret;
off += array->size;
array = (struct snd_soc_tplg_vendor_array *)
(tplg_w->priv.data + off);
ret = skl_tplg_get_desc_blocks(dev, array);
if (ret < 0)
return ret;
block_size = ret;
off += array->size;
array = (struct snd_soc_tplg_vendor_array *)
(tplg_w->priv.data + off);
data = (tplg_w->priv.data + off);
if (block_type == SKL_TYPE_TUPLE) {
ret = skl_tplg_get_tokens(dev, data,
skl, mconfig, block_size);
if (ret < 0)
return ret;
--num_blocks;
} else {
if (mconfig->formats_config.caps_size > 0)
memcpy(mconfig->formats_config.caps, data,
mconfig->formats_config.caps_size);
--num_blocks;
}
} }
return 0;
} }
static void skl_clear_pin_config(struct snd_soc_platform *platform, static void skl_clear_pin_config(struct snd_soc_platform *platform,
...@@ -1606,9 +2132,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, ...@@ -1606,9 +2132,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
struct skl *skl = ebus_to_skl(ebus); struct skl *skl = ebus_to_skl(ebus);
struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl_module_cfg *mconfig; struct skl_module_cfg *mconfig;
struct skl_pipe *pipe;
struct skl_dfw_module *dfw_config =
(struct skl_dfw_module *)tplg_w->priv.data;
if (!tplg_w->priv.size) if (!tplg_w->priv.size)
goto bind_event; goto bind_event;
...@@ -1619,76 +2142,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, ...@@ -1619,76 +2142,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
return -ENOMEM; return -ENOMEM;
w->priv = mconfig; w->priv = mconfig;
memcpy(&mconfig->guid, &dfw_config->uuid, 16);
ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config); /*
* module binary can be loaded later, so set it to query when
* module is load for a use case
*/
mconfig->id.module_id = -1;
/* Parse private data for tuples */
ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig);
if (ret < 0) if (ret < 0)
return ret; return ret;
mconfig->id.module_id = dfw_config->module_id;
mconfig->id.instance_id = dfw_config->instance_id;
mconfig->mcps = dfw_config->max_mcps;
mconfig->ibs = dfw_config->ibs;
mconfig->obs = dfw_config->obs;
mconfig->core_id = dfw_config->core_id;
mconfig->max_in_queue = dfw_config->max_in_queue;
mconfig->max_out_queue = dfw_config->max_out_queue;
mconfig->is_loadable = dfw_config->is_loadable;
skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt,
MODULE_MAX_IN_PINS);
skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt,
MODULE_MAX_OUT_PINS);
mconfig->params_fixup = dfw_config->params_fixup;
mconfig->converter = dfw_config->converter;
mconfig->m_type = dfw_config->module_type;
mconfig->vbus_id = dfw_config->vbus_id;
mconfig->mem_pages = dfw_config->mem_pages;
pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
if (pipe)
mconfig->pipe = pipe;
mconfig->dev_type = dfw_config->dev_type;
mconfig->hw_conn_type = dfw_config->hw_conn_type;
mconfig->time_slot = dfw_config->time_slot;
mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
sizeof(*mconfig->m_in_pin),
GFP_KERNEL);
if (!mconfig->m_in_pin)
return -ENOMEM;
mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
sizeof(*mconfig->m_out_pin),
GFP_KERNEL);
if (!mconfig->m_out_pin)
return -ENOMEM;
skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
dfw_config->is_dynamic_in_pin,
mconfig->max_in_queue);
skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
dfw_config->is_dynamic_out_pin,
mconfig->max_out_queue);
if (mconfig->formats_config.caps_size == 0)
goto bind_event;
mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
mconfig->formats_config.caps_size, GFP_KERNEL);
if (mconfig->formats_config.caps == NULL)
return -ENOMEM;
memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
dfw_config->caps.caps_size);
mconfig->formats_config.param_id = dfw_config->caps.param_id;
mconfig->formats_config.set_params = dfw_config->caps.set_params;
bind_event: bind_event:
if (tplg_w->event_type == 0) { if (tplg_w->event_type == 0) {
dev_dbg(bus->dev, "ASoC: No event handler required\n"); dev_dbg(bus->dev, "ASoC: No event handler required\n");
...@@ -1767,11 +2231,229 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, ...@@ -1767,11 +2231,229 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
return 0; return 0;
} }
static int skl_tplg_fill_str_mfest_tkn(struct device *dev,
struct snd_soc_tplg_vendor_string_elem *str_elem,
struct skl_dfw_manifest *minfo)
{
int tkn_count = 0;
static int ref_count;
switch (str_elem->token) {
case SKL_TKN_STR_LIB_NAME:
if (ref_count > minfo->lib_count - 1) {
ref_count = 0;
return -EINVAL;
}
strncpy(minfo->lib[ref_count].name, str_elem->string,
ARRAY_SIZE(minfo->lib[ref_count].name));
ref_count++;
tkn_count++;
break;
default:
dev_err(dev, "Not a string token %d\n", str_elem->token);
break;
}
return tkn_count;
}
static int skl_tplg_get_str_tkn(struct device *dev,
struct snd_soc_tplg_vendor_array *array,
struct skl_dfw_manifest *minfo)
{
int tkn_count = 0, ret;
struct snd_soc_tplg_vendor_string_elem *str_elem;
str_elem = (struct snd_soc_tplg_vendor_string_elem *)array->value;
while (tkn_count < array->num_elems) {
ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, minfo);
str_elem++;
if (ret < 0)
return ret;
tkn_count = tkn_count + ret;
}
return tkn_count;
}
static int skl_tplg_get_int_tkn(struct device *dev,
struct snd_soc_tplg_vendor_value_elem *tkn_elem,
struct skl_dfw_manifest *minfo)
{
int tkn_count = 0;
switch (tkn_elem->token) {
case SKL_TKN_U32_LIB_COUNT:
minfo->lib_count = tkn_elem->value;
tkn_count++;
break;
default:
dev_err(dev, "Not a manifest token %d\n", tkn_elem->token);
return -EINVAL;
}
return tkn_count;
}
/*
* Fill the manifest structure by parsing the tokens based on the
* type.
*/
static int skl_tplg_get_manifest_tkn(struct device *dev,
char *pvt_data, struct skl_dfw_manifest *minfo,
int block_size)
{
int tkn_count = 0, ret;
int off = 0, tuple_size = 0;
struct snd_soc_tplg_vendor_array *array;
struct snd_soc_tplg_vendor_value_elem *tkn_elem;
if (block_size <= 0)
return -EINVAL;
while (tuple_size < block_size) {
array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off);
off += array->size;
switch (array->type) {
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
ret = skl_tplg_get_str_tkn(dev, array, minfo);
if (ret < 0)
return ret;
tkn_count += ret;
tuple_size += tkn_count *
sizeof(struct snd_soc_tplg_vendor_string_elem);
continue;
case SND_SOC_TPLG_TUPLE_TYPE_UUID:
dev_warn(dev, "no uuid tokens for skl tplf manifest\n");
continue;
default:
tkn_elem = array->value;
tkn_count = 0;
break;
}
while (tkn_count <= array->num_elems - 1) {
ret = skl_tplg_get_int_tkn(dev,
tkn_elem, minfo);
if (ret < 0)
return ret;
tkn_count = tkn_count + ret;
tkn_elem++;
tuple_size += tkn_count *
sizeof(struct snd_soc_tplg_vendor_value_elem);
break;
}
tkn_count = 0;
}
return 0;
}
/*
* Parse manifest private data for tokens. The private data block is
* preceded by descriptors for type and size of data block.
*/
static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,
struct device *dev, struct skl_dfw_manifest *minfo)
{
struct snd_soc_tplg_vendor_array *array;
int num_blocks, block_size = 0, block_type, off = 0;
char *data;
int ret;
/* Read the NUM_DATA_BLOCKS descriptor */
array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data;
ret = skl_tplg_get_desc_blocks(dev, array);
if (ret < 0)
return ret;
num_blocks = ret;
off += array->size;
array = (struct snd_soc_tplg_vendor_array *)
(manifest->priv.data + off);
/* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */
while (num_blocks > 0) {
ret = skl_tplg_get_desc_blocks(dev, array);
if (ret < 0)
return ret;
block_type = ret;
off += array->size;
array = (struct snd_soc_tplg_vendor_array *)
(manifest->priv.data + off);
ret = skl_tplg_get_desc_blocks(dev, array);
if (ret < 0)
return ret;
block_size = ret;
off += array->size;
array = (struct snd_soc_tplg_vendor_array *)
(manifest->priv.data + off);
data = (manifest->priv.data + off);
if (block_type == SKL_TYPE_TUPLE) {
ret = skl_tplg_get_manifest_tkn(dev, data, minfo,
block_size);
if (ret < 0)
return ret;
--num_blocks;
} else {
return -EINVAL;
}
}
return 0;
}
static int skl_manifest_load(struct snd_soc_component *cmpnt,
struct snd_soc_tplg_manifest *manifest)
{
struct skl_dfw_manifest *minfo;
struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl *skl = ebus_to_skl(ebus);
int ret = 0;
/* proceed only if we have private data defined */
if (manifest->priv.size == 0)
return 0;
minfo = &skl->skl_sst->manifest;
skl_tplg_get_manifest_data(manifest, bus->dev, minfo);
if (minfo->lib_count > HDA_MAX_LIB) {
dev_err(bus->dev, "Exceeding max Library count. Got:%d\n",
minfo->lib_count);
ret = -EINVAL;
}
return ret;
}
static struct snd_soc_tplg_ops skl_tplg_ops = { static struct snd_soc_tplg_ops skl_tplg_ops = {
.widget_load = skl_tplg_widget_load, .widget_load = skl_tplg_widget_load,
.control_load = skl_tplg_control_load, .control_load = skl_tplg_control_load,
.bytes_ext_ops = skl_tlv_ops, .bytes_ext_ops = skl_tlv_ops,
.bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
.manifest = skl_manifest_load,
}; };
/* /*
......
...@@ -133,7 +133,7 @@ struct skl_i2s_config_blob { ...@@ -133,7 +133,7 @@ struct skl_i2s_config_blob {
struct skl_dma_control { struct skl_dma_control {
u32 node_id; u32 node_id;
u32 config_length; u32 config_length;
u32 config_data[1]; u32 config_data[0];
} __packed; } __packed;
struct skl_cpr_cfg { struct skl_cpr_cfg {
...@@ -215,9 +215,20 @@ struct skl_module_fmt { ...@@ -215,9 +215,20 @@ struct skl_module_fmt {
struct skl_module_cfg; 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 { struct skl_module_inst_id {
u32 module_id; int module_id;
u32 instance_id; u32 instance_id;
int pvt_id;
}; };
enum skl_module_pin_state { enum skl_module_pin_state {
......
...@@ -80,7 +80,8 @@ enum skl_module_type { ...@@ -80,7 +80,8 @@ enum skl_module_type {
SKL_MODULE_TYPE_UPDWMIX, SKL_MODULE_TYPE_UPDWMIX,
SKL_MODULE_TYPE_SRCINT, SKL_MODULE_TYPE_SRCINT,
SKL_MODULE_TYPE_ALGO, SKL_MODULE_TYPE_ALGO,
SKL_MODULE_TYPE_BASE_OUTFMT SKL_MODULE_TYPE_BASE_OUTFMT,
SKL_MODULE_TYPE_KPB,
}; };
enum skl_core_affinity { enum skl_core_affinity {
...@@ -148,84 +149,34 @@ enum skl_module_param_type { ...@@ -148,84 +149,34 @@ enum skl_module_param_type {
SKL_PARAM_BIND SKL_PARAM_BIND
}; };
struct skl_dfw_module_pin { struct skl_dfw_algo_data {
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 set_params:2;
u32 rsvd:30; u32 rsvd:30;
u32 param_id; u32 param_id;
u32 caps_size; u32 max;
u32 caps[HDA_SST_CFG_MAX]; char params[0];
};
struct skl_dfw_pipe {
u8 pipe_id;
u8 pipe_priority;
u16 conn_type:4;
u16 rsvd:4;
u16 memory_pages:8;
} __packed; } __packed;
struct skl_dfw_module { #define LIB_NAME_LENGTH 128
u8 uuid[16]; #define HDA_MAX_LIB 16
u16 module_id; struct lib_info {
u16 instance_id; char name[LIB_NAME_LENGTH];
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; } __packed;
struct skl_dfw_algo_data { struct skl_dfw_manifest {
u32 set_params:2; u32 lib_count;
u32 rsvd:30; struct lib_info lib[HDA_MAX_LIB];
u32 param_id;
u32 max;
char params[0];
} __packed; } __packed;
enum skl_tkn_dir {
SKL_DIR_IN,
SKL_DIR_OUT
};
enum skl_tuple_type {
SKL_TYPE_TUPLE,
SKL_TYPE_DATA
};
#endif #endif
...@@ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) ...@@ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
return -ENXIO; return -ENXIO;
} }
snd_hdac_ext_bus_parse_capabilities(ebus); snd_hdac_bus_parse_capabilities(bus);
if (skl_acquire_irq(ebus, 0) < 0) if (skl_acquire_irq(ebus, 0) < 0)
return -EBUSY; return -EBUSY;
...@@ -684,7 +684,7 @@ static int skl_probe(struct pci_dev *pci, ...@@ -684,7 +684,7 @@ static int skl_probe(struct pci_dev *pci,
skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
/* check if dsp is there */ /* check if dsp is there */
if (ebus->ppcap) { if (bus->ppcap) {
err = skl_machine_device_register(skl, err = skl_machine_device_register(skl,
(void *)pci_id->driver_data); (void *)pci_id->driver_data);
if (err < 0) if (err < 0)
...@@ -698,7 +698,7 @@ static int skl_probe(struct pci_dev *pci, ...@@ -698,7 +698,7 @@ static int skl_probe(struct pci_dev *pci,
skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
} }
if (ebus->mlcap) if (bus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus); snd_hdac_ext_bus_get_ml_capabilities(ebus);
/* create device for soc dmic */ /* create device for soc dmic */
......
...@@ -105,6 +105,7 @@ struct skl_dsp_ops { ...@@ -105,6 +105,7 @@ struct skl_dsp_ops {
int irq, const char *fw_name, int irq, const char *fw_name,
struct skl_dsp_loader_ops loader_ops, struct skl_dsp_loader_ops loader_ops,
struct skl_sst **skl_sst); struct skl_sst **skl_sst);
int (*init_fw)(struct device *dev, struct skl_sst *ctx);
void (*cleanup)(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); ...@@ -123,4 +124,5 @@ int skl_free_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl);
void skl_cleanup_resources(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 */ #endif /* __SOUND_SOC_SKL_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment