提交 bea8205f 编写于 作者: M Mark Brown

Merge branch 'asoc-fix-ep93xx' into spi-fix-ep93xx

Texas Instruments DaVinci McBSP module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This binding describes the "Multi-channel Buffered Serial Port" (McBSP)
audio interface found in some TI DaVinci processors like the OMAP-L138 or AM180x.
Required properties:
~~~~~~~~~~~~~~~~~~~~
- compatible :
"ti,da850-mcbsp" : for DA850, AM180x and OPAM-L138 platforms
- reg : physical base address and length of the controller memory mapped
region(s).
- reg-names : Should contain:
* "mpu" for the main registers (required).
* "dat" for the data FIFO (optional).
- dmas: three element list of DMA controller phandles, DMA request line and
TC channel ordered triplets.
- dma-names: identifier string for each DMA request line in the dmas property.
These strings correspond 1:1 with the ordered pairs in dmas. The dma
identifiers must be "rx" and "tx".
Optional properties:
~~~~~~~~~~~~~~~~~~~~
- interrupts : Interrupt numbers for McBSP
- interrupt-names : Known interrupt names are "rx" and "tx"
- pinctrl-0: Should specify pin control group used for this controller.
- pinctrl-names: Should contain only one value - "default", for more details
please refer to pinctrl-bindings.txt
Example (AM1808):
~~~~~~~~~~~~~~~~~
mcbsp0: mcbsp@1d10000 {
compatible = "ti,da850-mcbsp";
pinctrl-names = "default";
pinctrl-0 = <&mcbsp0_pins>;
reg = <0x00110000 0x1000>,
<0x00310000 0x1000>;
reg-names = "mpu", "dat";
interrupts = <97 98>;
interrupts-names = "rx", "tx";
dmas = <&edma0 3 1
&edma0 2 1>;
dma-names = "tx", "rx";
status = "okay";
};
...@@ -7,8 +7,8 @@ codec/DSP interfaces. ...@@ -7,8 +7,8 @@ codec/DSP interfaces.
Required properties: Required properties:
- compatible : Compatible list, contains "fsl,vf610-sai" or - compatible : Compatible list, contains "fsl,vf610-sai",
"fsl,imx6sx-sai". "fsl,imx6sx-sai" or "fsl,imx6ul-sai"
- reg : Offset and length of the register set for the device. - reg : Offset and length of the register set for the device.
...@@ -48,6 +48,11 @@ Required properties: ...@@ -48,6 +48,11 @@ Required properties:
receive data by following their own bit clocks and receive data by following their own bit clocks and
frame sync clocks separately. frame sync clocks separately.
Optional properties (for mx6ul):
- fsl,sai-mclk-direction-output: This is a boolean property. If present,
indicates that SAI will output the SAI MCLK clock.
Note: Note:
- If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
default synchronous mode (sync Rx with Tx) will be used, which means both default synchronous mode (sync Rx with Tx) will be used, which means both
......
PCM5102a audio CODECs
These devices does not use I2C or SPI.
Required properties:
- compatible : set as "ti,pcm5102a"
Examples:
pcm5102a: pcm5102a {
compatible = "ti,pcm5102a";
};
...@@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS ...@@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS
M: Timur Tabi <timur@tabi.org> M: Timur Tabi <timur@tabi.org>
M: Nicolin Chen <nicoleotsuka@gmail.com> M: Nicolin Chen <nicoleotsuka@gmail.com>
M: Xiubo Li <Xiubo.Lee@gmail.com> M: Xiubo Li <Xiubo.Lee@gmail.com>
R: Fabio Estevam <fabio.estevam@nxp.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: linuxppc-dev@lists.ozlabs.org L: linuxppc-dev@lists.ozlabs.org
S: Maintained S: Maintained
......
...@@ -567,7 +567,7 @@ static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi) ...@@ -567,7 +567,7 @@ static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
txd = ep93xx_spi_dma_prepare(espi, DMA_MEM_TO_DEV); txd = ep93xx_spi_dma_prepare(espi, DMA_MEM_TO_DEV);
if (IS_ERR(txd)) { if (IS_ERR(txd)) {
ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM); ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM);
dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd)); dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(txd));
msg->status = PTR_ERR(txd); msg->status = PTR_ERR(txd);
return; return;
} }
......
...@@ -447,5 +447,11 @@ ...@@ -447,5 +447,11 @@
#define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18) #define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18)
#define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17) #define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17)
#define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17) #define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17)
#define IMX6UL_GPR1_SAI1_MCLK_DIR (0x1 << 19)
#define IMX6UL_GPR1_SAI2_MCLK_DIR (0x1 << 20)
#define IMX6UL_GPR1_SAI3_MCLK_DIR (0x1 << 21)
#define IMX6UL_GPR1_SAI_MCLK_MASK (0x7 << 19)
#define MCLK_DIR(x) (x == 1 ? IMX6UL_GPR1_SAI1_MCLK_DIR : x == 2 ? \
IMX6UL_GPR1_SAI2_MCLK_DIR : IMX6UL_GPR1_SAI3_MCLK_DIR)
#endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */ #endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */
...@@ -51,6 +51,16 @@ struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, ...@@ -51,6 +51,16 @@ struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
void *filter_data); void *filter_data);
struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream); struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream);
/*
* The DAI supports packed transfers, eg 2 16-bit samples in a 32-bit word.
* If this flag is set the dmaengine driver won't put any restriction on
* the supported sample formats and set the DMA transfer size to undefined.
* The DAI driver is responsible to disable any unsupported formats in it's
* configuration and catch corner cases that are not already handled in
* the ALSA core.
*/
#define SND_DMAENGINE_PCM_DAI_FLAG_PACK BIT(0)
/** /**
* struct snd_dmaengine_dai_dma_data - DAI DMA configuration data * struct snd_dmaengine_dai_dma_data - DAI DMA configuration data
* @addr: Address of the DAI data source or destination register. * @addr: Address of the DAI data source or destination register.
...@@ -63,6 +73,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) ...@@ -63,6 +73,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
* requesting the DMA channel. * requesting the DMA channel.
* @chan_name: Custom channel name to use when requesting DMA channel. * @chan_name: Custom channel name to use when requesting DMA channel.
* @fifo_size: FIFO size of the DAI controller in bytes * @fifo_size: FIFO size of the DAI controller in bytes
* @flags: PCM_DAI flags, only SND_DMAENGINE_PCM_DAI_FLAG_PACK for now
*/ */
struct snd_dmaengine_dai_dma_data { struct snd_dmaengine_dai_dma_data {
dma_addr_t addr; dma_addr_t addr;
...@@ -72,6 +83,7 @@ struct snd_dmaengine_dai_dma_data { ...@@ -72,6 +83,7 @@ struct snd_dmaengine_dai_dma_data {
void *filter_data; void *filter_data;
const char *chan_name; const char *chan_name;
unsigned int fifo_size; unsigned int fifo_size;
unsigned int flags;
}; };
void snd_dmaengine_pcm_set_config_from_dai_data( void snd_dmaengine_pcm_set_config_from_dai_data(
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
* @gtscap: gts capabilities pointer * @gtscap: gts capabilities pointer
* @drsmcap: dma resume 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
* @cmd_dma_state: state of cmd DMAs: CORB and RIRB
*/ */
struct hdac_ext_bus { struct hdac_ext_bus {
struct hdac_bus bus; struct hdac_bus bus;
...@@ -27,6 +29,9 @@ struct hdac_ext_bus { ...@@ -27,6 +29,9 @@ struct hdac_ext_bus {
void __iomem *drsmcap; void __iomem *drsmcap;
struct list_head hlink_list; struct list_head hlink_list;
struct mutex lock;
bool cmd_dma_state;
}; };
int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev, int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev,
...@@ -142,6 +147,9 @@ struct hdac_ext_link { ...@@ -142,6 +147,9 @@ struct hdac_ext_link {
void __iomem *ml_addr; /* link output stream reg pointer */ void __iomem *ml_addr; /* link output stream reg pointer */
u32 lcaps; /* link capablities */ u32 lcaps; /* link capablities */
u16 lsdiid; /* link sdi identifier */ u16 lsdiid; /* link sdi identifier */
int ref_count;
struct list_head list; struct list_head list;
}; };
...@@ -154,6 +162,11 @@ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, ...@@ -154,6 +162,11 @@ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
int stream); int stream);
int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
struct hdac_ext_link *link);
int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
struct hdac_ext_link *link);
/* update register macro */ /* update register macro */
#define snd_hdac_updatel(addr, reg, mask, val) \ #define snd_hdac_updatel(addr, reg, mask, val) \
writel(((readl(addr + reg) & ~(mask)) | (val)), \ writel(((readl(addr + reg) & ~(mask)) | (val)), \
......
/*
* hdmi-codec.h - HDMI Codec driver API
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
*
* Author: Jyri Sarha <jsarha@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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 __HDMI_CODEC_H__
#define __HDMI_CODEC_H__
#include <linux/hdmi.h>
#include <drm/drm_edid.h>
#include <sound/asoundef.h>
#include <uapi/sound/asound.h>
/*
* Protocol between ASoC cpu-dai and HDMI-encoder
*/
struct hdmi_codec_daifmt {
enum {
HDMI_I2S,
HDMI_RIGHT_J,
HDMI_LEFT_J,
HDMI_DSP_A,
HDMI_DSP_B,
HDMI_AC97,
HDMI_SPDIF,
} fmt;
int bit_clk_inv:1;
int frame_clk_inv:1;
int bit_clk_master:1;
int frame_clk_master:1;
};
/*
* HDMI audio parameters
*/
struct hdmi_codec_params {
struct hdmi_audio_infoframe cea;
struct snd_aes_iec958 iec;
int sample_rate;
int sample_width;
int channels;
};
struct hdmi_codec_ops {
/*
* Called when ASoC starts an audio stream setup.
* Optional
*/
int (*audio_startup)(struct device *dev);
/*
* Configures HDMI-encoder for audio stream.
* Mandatory
*/
int (*hw_params)(struct device *dev,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms);
/*
* Shuts down the audio stream.
* Mandatory
*/
void (*audio_shutdown)(struct device *dev);
/*
* Mute/unmute HDMI audio stream.
* Optional
*/
int (*digital_mute)(struct device *dev, bool enable);
/*
* Provides EDID-Like-Data from connected HDMI device.
* Optional
*/
int (*get_eld)(struct device *dev, uint8_t *buf, size_t len);
};
/* HDMI codec initalization data */
struct hdmi_codec_pdata {
const struct hdmi_codec_ops *ops;
uint i2s:1;
uint spdif:1;
int max_i2s_channels;
};
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */
...@@ -6,4 +6,6 @@ ...@@ -6,4 +6,6 @@
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len); size_t len);
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len);
#endif #endif
...@@ -100,6 +100,7 @@ struct device; ...@@ -100,6 +100,7 @@ struct device;
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = wncontrols} .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
/* DEPRECATED: use SND_SOC_DAPM_SUPPLY */
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_micbias, .name = wname, \ { .id = snd_soc_dapm_micbias, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
...@@ -473,7 +474,7 @@ enum snd_soc_dapm_type { ...@@ -473,7 +474,7 @@ enum snd_soc_dapm_type {
snd_soc_dapm_out_drv, /* output driver */ snd_soc_dapm_out_drv, /* output driver */
snd_soc_dapm_adc, /* analog to digital converter */ snd_soc_dapm_adc, /* analog to digital converter */
snd_soc_dapm_dac, /* digital to analog converter */ snd_soc_dapm_dac, /* digital to analog converter */
snd_soc_dapm_micbias, /* microphone bias (power) */ snd_soc_dapm_micbias, /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */
snd_soc_dapm_mic, /* microphone */ snd_soc_dapm_mic, /* microphone */
snd_soc_dapm_hp, /* headphones */ snd_soc_dapm_hp, /* headphones */
snd_soc_dapm_spk, /* speaker */ snd_soc_dapm_spk, /* speaker */
......
...@@ -1002,7 +1002,7 @@ struct snd_soc_dai_link { ...@@ -1002,7 +1002,7 @@ struct snd_soc_dai_link {
*/ */
const char *platform_name; const char *platform_name;
struct device_node *platform_of_node; struct device_node *platform_of_node;
int be_id; /* optional ID for machine driver BE identification */ int id; /* optional ID for machine driver link identification */
const struct snd_soc_pcm_stream *params; const struct snd_soc_pcm_stream *params;
unsigned int num_params; unsigned int num_params;
...@@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card, ...@@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card,
int snd_soc_register_dai(struct snd_soc_component *component, int snd_soc_register_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv); struct snd_soc_dai_driver *dai_drv);
struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc);
#include <sound/soc-dai.h> #include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
......
...@@ -106,8 +106,9 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); ...@@ -106,8 +106,9 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
* direction of the substream. If the substream is a playback stream the dst * direction of the substream. If the substream is a playback stream the dst
* fields will be initialized, if it is a capture stream the src fields will be * fields will be initialized, if it is a capture stream the src fields will be
* initialized. The {dst,src}_addr_width field will only be initialized if the * initialized. The {dst,src}_addr_width field will only be initialized if the
* addr_width field of the DAI DMA data struct is not equal to * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
* DMA_SLAVE_BUSWIDTH_UNDEFINED. * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
* both conditions are met the latter takes priority.
*/ */
void snd_dmaengine_pcm_set_config_from_dai_data( void snd_dmaengine_pcm_set_config_from_dai_data(
const struct snd_pcm_substream *substream, const struct snd_pcm_substream *substream,
...@@ -117,11 +118,17 @@ void snd_dmaengine_pcm_set_config_from_dai_data( ...@@ -117,11 +118,17 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
slave_config->dst_addr = dma_data->addr; slave_config->dst_addr = dma_data->addr;
slave_config->dst_maxburst = dma_data->maxburst; slave_config->dst_maxburst = dma_data->maxburst;
if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
slave_config->dst_addr_width =
DMA_SLAVE_BUSWIDTH_UNDEFINED;
if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
slave_config->dst_addr_width = dma_data->addr_width; slave_config->dst_addr_width = dma_data->addr_width;
} else { } else {
slave_config->src_addr = dma_data->addr; slave_config->src_addr = dma_data->addr;
slave_config->src_maxburst = dma_data->maxburst; slave_config->src_maxburst = dma_data->maxburst;
if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
slave_config->src_addr_width =
DMA_SLAVE_BUSWIDTH_UNDEFINED;
if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
slave_config->src_addr_width = dma_data->addr_width; slave_config->src_addr_width = dma_data->addr_width;
} }
......
...@@ -9,30 +9,18 @@ ...@@ -9,30 +9,18 @@
#include <linux/types.h> #include <linux/types.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/pcm_iec958.h> #include <sound/pcm_iec958.h>
/** static int create_iec958_consumer(uint rate, uint sample_width,
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status u8 *cs, size_t len)
* @runtime: pcm runtime structure with ->rate filled in
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Create the consumer format channel status data in @cs of maximum size
* @len corresponding to the parameters of the PCM runtime @runtime.
*
* Drivers may wish to tweak the contents of the buffer after creation.
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len)
{ {
unsigned int fs, ws; unsigned int fs, ws;
if (len < 4) if (len < 4)
return -EINVAL; return -EINVAL;
switch (runtime->rate) { switch (rate) {
case 32000: case 32000:
fs = IEC958_AES3_CON_FS_32000; fs = IEC958_AES3_CON_FS_32000;
break; break;
...@@ -59,7 +47,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, ...@@ -59,7 +47,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
} }
if (len > 4) { if (len > 4) {
switch (snd_pcm_format_width(runtime->format)) { switch (sample_width) {
case 16: case 16:
ws = IEC958_AES4_CON_WORDLEN_20_16; ws = IEC958_AES4_CON_WORDLEN_20_16;
break; break;
...@@ -71,6 +59,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, ...@@ -71,6 +59,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
IEC958_AES4_CON_MAX_WORDLEN_24; IEC958_AES4_CON_MAX_WORDLEN_24;
break; break;
case 24: case 24:
case 32: /* Assume 24-bit width for 32-bit samples. */
ws = IEC958_AES4_CON_WORDLEN_24_20 | ws = IEC958_AES4_CON_WORDLEN_24_20 |
IEC958_AES4_CON_MAX_WORDLEN_24; IEC958_AES4_CON_MAX_WORDLEN_24;
break; break;
...@@ -92,4 +81,46 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, ...@@ -92,4 +81,46 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
return len; return len;
} }
/**
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
* @runtime: pcm runtime structure with ->rate filled in
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Create the consumer format channel status data in @cs of maximum size
* @len corresponding to the parameters of the PCM runtime @runtime.
*
* Drivers may wish to tweak the contents of the buffer after creation.
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len)
{
return create_iec958_consumer(runtime->rate,
snd_pcm_format_width(runtime->format),
cs, len);
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer); EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
/**
* snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status
* @hw_params: the hw_params instance for extracting rate and sample format
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Create the consumer format channel status data in @cs of maximum size
* @len corresponding to the parameters of the PCM runtime @runtime.
*
* Drivers may wish to tweak the contents of the buffer after creation.
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len)
{
return create_iec958_consumer(params_rate(params), params_width(params),
cs, len);
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
...@@ -105,6 +105,9 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, ...@@ -105,6 +105,9 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
INIT_LIST_HEAD(&ebus->hlink_list); INIT_LIST_HEAD(&ebus->hlink_list);
ebus->idx = idx++; ebus->idx = idx++;
mutex_init(&ebus->lock);
ebus->cmd_dma_state = true;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
......
...@@ -186,6 +186,9 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) ...@@ -186,6 +186,9 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
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);
/* since link in On, update the ref */
hlink->ref_count = 1;
list_add_tail(&hlink->list, &ebus->hlink_list); list_add_tail(&hlink->list, &ebus->hlink_list);
} }
...@@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) ...@@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
struct hdac_ext_link *link)
{
int ret = 0;
mutex_lock(&ebus->lock);
/*
* if we move from 0 to 1, count will be 1 so power up this link
* as well, also check the dma status and trigger that
*/
if (++link->ref_count == 1) {
if (!ebus->cmd_dma_state) {
snd_hdac_bus_init_cmd_io(&ebus->bus);
ebus->cmd_dma_state = true;
}
ret = snd_hdac_ext_bus_link_power_up(link);
}
mutex_unlock(&ebus->lock);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
struct hdac_ext_link *link)
{
int ret = 0;
struct hdac_ext_link *hlink;
bool link_up = false;
mutex_lock(&ebus->lock);
/*
* if we move from 1 to 0, count will be 0
* so power down this link as well
*/
if (--link->ref_count == 0) {
ret = snd_hdac_ext_bus_link_power_down(link);
/*
* now check if all links are off, if so turn off
* cmd dma as well
*/
list_for_each_entry(hlink, &ebus->hlink_list, list) {
if (hlink->ref_count) {
link_up = true;
break;
}
}
if (!link_up) {
snd_hdac_bus_stop_cmd_io(&ebus->bus);
ebus->cmd_dma_state = false;
}
}
mutex_unlock(&ebus->lock);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
...@@ -16,6 +16,16 @@ static inline int get_wcaps_type(unsigned int wcaps) ...@@ -16,6 +16,16 @@ static inline int get_wcaps_type(unsigned int wcaps)
return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
} }
static inline unsigned int get_wcaps_channels(u32 wcaps)
{
unsigned int chans;
chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
chans = (chans + 1) * 2;
return chans;
}
extern const struct attribute_group *hdac_dev_attr_groups[]; extern const struct attribute_group *hdac_dev_attr_groups[];
int hda_widget_sysfs_init(struct hdac_device *codec); int hda_widget_sysfs_init(struct hdac_device *codec);
void hda_widget_sysfs_exit(struct hdac_device *codec); void hda_widget_sysfs_exit(struct hdac_device *codec);
......
...@@ -652,7 +652,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, ...@@ -652,7 +652,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
| SSC_BF(RCMR_STTDLY, 1) | SSC_BF(RCMR_STTDLY, 1)
| SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_START, SSC_START_RISING_RF)
| SSC_BF(RCMR_CKI, SSC_CKI_FALLING) | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
| SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
| SSC_BF(RCMR_CKS, SSC_CKS_DIV); | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
...@@ -692,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, ...@@ -692,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
rcmr = SSC_BF(RCMR_PERIOD, 0) rcmr = SSC_BF(RCMR_PERIOD, 0)
| SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_STTDLY, START_DELAY)
| SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_START, SSC_START_RISING_RF)
| SSC_BF(RCMR_CKI, SSC_CKI_FALLING) | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
| SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
SSC_CKS_PIN : SSC_CKS_CLOCK); SSC_CKS_PIN : SSC_CKS_CLOCK);
......
...@@ -206,8 +206,8 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -206,8 +206,8 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
stype = substream->stream; stype = substream->stream;
pcd = to_dmadata(substream); pcd = to_dmadata(substream);
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d " DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
"runtime->min_align %d\n", "runtime->min_align %lu\n",
(unsigned long)runtime->dma_area, (unsigned long)runtime->dma_area,
(unsigned long)runtime->dma_addr, runtime->dma_bytes, (unsigned long)runtime->dma_addr, runtime->dma_bytes,
runtime->min_align); runtime->min_align);
......
...@@ -259,6 +259,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -259,6 +259,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
data_length = 16; data_length = 16;
break; break;
case SNDRV_PCM_FORMAT_S24_LE:
data_length = 24;
break;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
data_length = 32; data_length = 32;
break; break;
...@@ -273,13 +276,20 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -273,13 +276,20 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
/* otherwise calculate a fitting block ratio */ /* otherwise calculate a fitting block ratio */
bclk_ratio = 2 * data_length; bclk_ratio = 2 * data_length;
/* set target clock rate*/ /* Clock should only be set up here if CPU is clock master */
clk_set_rate(dev->clk, sampling_rate * bclk_ratio); switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
break;
default:
break;
}
/* Setup the frame format */ /* Setup the frame format */
format = BCM2835_I2S_CHEN; format = BCM2835_I2S_CHEN;
if (data_length > 24) if (data_length >= 24)
format |= BCM2835_I2S_CHWEX; format |= BCM2835_I2S_CHWEX;
format |= BCM2835_I2S_CHWID((data_length-8)&0xf); format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
...@@ -570,6 +580,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { ...@@ -570,6 +580,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE .formats = SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_LE
}, },
.capture = { .capture = {
...@@ -577,6 +588,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { ...@@ -577,6 +588,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE .formats = SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_LE
}, },
.ops = &bcm2835_i2s_dai_ops, .ops = &bcm2835_i2s_dai_ops,
...@@ -678,6 +690,15 @@ static int bcm2835_i2s_probe(struct platform_device *pdev) ...@@ -678,6 +690,15 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
/*
* Set the PACK flag to enable S16_LE support (2 S16_LE values
* packed into 32-bit transfers).
*/
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags =
SND_DMAENGINE_PCM_DAI_FLAG_PACK;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags =
SND_DMAENGINE_PCM_DAI_FLAG_PACK;
/* BCLK ratio - use default */ /* BCLK ratio - use default */
dev->bclk_ratio = 0; dev->bclk_ratio = 0;
......
...@@ -88,12 +88,14 @@ config SND_SOC_ALL_CODECS ...@@ -88,12 +88,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C select SND_SOC_ML26124 if I2C
select SND_SOC_NAU8825 if I2C select SND_SOC_NAU8825 if I2C
select SND_SOC_HDMI_CODEC
select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM179X_I2C if I2C select SND_SOC_PCM179X_I2C if I2C
select SND_SOC_PCM179X_SPI if SPI_MASTER select SND_SOC_PCM179X_SPI if SPI_MASTER
select SND_SOC_PCM3008 select SND_SOC_PCM3008
select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_I2C if I2C
select SND_SOC_PCM3168A_SPI if SPI_MASTER select SND_SOC_PCM3168A_SPI if SPI_MASTER
select SND_SOC_PCM5102A
select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C select SND_SOC_RT286 if I2C
...@@ -477,6 +479,11 @@ config SND_SOC_BT_SCO ...@@ -477,6 +479,11 @@ config SND_SOC_BT_SCO
config SND_SOC_DMIC config SND_SOC_DMIC
tristate tristate
config SND_SOC_HDMI_CODEC
tristate
select SND_PCM_ELD
select SND_PCM_IEC958
config SND_SOC_ES8328 config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC" tristate "Everest Semi ES8328 CODEC"
...@@ -575,6 +582,9 @@ config SND_SOC_PCM3168A_SPI ...@@ -575,6 +582,9 @@ config SND_SOC_PCM3168A_SPI
select SND_SOC_PCM3168A select SND_SOC_PCM3168A
select REGMAP_SPI select REGMAP_SPI
config SND_SOC_PCM5102A
tristate
config SND_SOC_PCM512x config SND_SOC_PCM512x
tristate tristate
......
...@@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o ...@@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o snd-soc-ml26124-objs := ml26124.o
snd-soc-nau8825-objs := nau8825.o snd-soc-nau8825-objs := nau8825.o
snd-soc-hdmi-codec-objs := hdmi-codec.o
snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm179x-codec-objs := pcm179x.o snd-soc-pcm179x-codec-objs := pcm179x.o
snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
...@@ -89,6 +90,7 @@ snd-soc-pcm3008-objs := pcm3008.o ...@@ -89,6 +90,7 @@ snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-objs := pcm3168a.o
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
snd-soc-pcm5102a-objs := pcm5102a.o
snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o
...@@ -290,6 +292,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o ...@@ -290,6 +292,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
...@@ -298,6 +301,7 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o ...@@ -298,6 +301,7 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
......
...@@ -608,9 +608,7 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev) ...@@ -608,9 +608,7 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
of_property_read_string(np, "clock-output-names", &clk_name); of_property_read_string(np, "clock-output-names", &clk_name);
clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate);
(parent_clk_name) ? 0 : CLK_IS_ROOT,
rate);
if (!IS_ERR(clk)) if (!IS_ERR(clk))
of_clk_add_provider(np, of_clk_src_simple_get, clk); of_clk_add_provider(np, of_clk_src_simple_get, clk);
......
...@@ -221,6 +221,8 @@ int arizona_init_spk(struct snd_soc_codec *codec) ...@@ -221,6 +221,8 @@ int arizona_init_spk(struct snd_soc_codec *codec)
switch (arizona->type) { switch (arizona->type) {
case WM8997: case WM8997:
case CS47L24:
case WM1831:
break; break;
default: default:
ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
...@@ -1134,7 +1136,6 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w, ...@@ -1134,7 +1136,6 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
int event) int event)
{ {
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
unsigned int mask = 0x3 << w->shift;
unsigned int val; unsigned int val;
switch (event) { switch (event) {
...@@ -1148,7 +1149,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w, ...@@ -1148,7 +1149,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
return 0; return 0;
} }
snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val); snd_soc_write(codec, ARIZONA_CLOCK_CONTROL, val);
return 0; return 0;
} }
...@@ -2047,7 +2048,21 @@ static int arizona_calc_fratio(struct arizona_fll *fll, ...@@ -2047,7 +2048,21 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
init_ratio, Fref, refdiv); init_ratio, Fref, refdiv);
while (div <= ARIZONA_FLL_MAX_REFDIV) { while (div <= ARIZONA_FLL_MAX_REFDIV) {
for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; /* start from init_ratio because this may already give a
* fractional N.K
*/
for (ratio = init_ratio; ratio > 0; ratio--) {
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
arizona_fll_dbg(fll,
"pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
Fref, refdiv, div, ratio);
return ratio;
}
}
for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO;
ratio++) { ratio++) {
if ((ARIZONA_FLL_VCO_CORNER / 2) / if ((ARIZONA_FLL_VCO_CORNER / 2) /
(fll->vco_mult * ratio) < Fref) { (fll->vco_mult * ratio) < Fref) {
...@@ -2073,17 +2088,6 @@ static int arizona_calc_fratio(struct arizona_fll *fll, ...@@ -2073,17 +2088,6 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
} }
} }
for (ratio = init_ratio - 1; ratio > 0; ratio--) {
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
arizona_fll_dbg(fll,
"pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
Fref, refdiv, div, ratio);
return ratio;
}
}
div *= 2; div *= 2;
Fref /= 2; Fref /= 2;
refdiv++; refdiv++;
......
...@@ -56,7 +56,7 @@ struct cs42l56_private { ...@@ -56,7 +56,7 @@ struct cs42l56_private {
u8 iface; u8 iface;
u8 iface_fmt; u8 iface_fmt;
u8 iface_inv; u8 iface_inv;
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) #if IS_ENABLED(CONFIG_INPUT)
struct input_dev *beep; struct input_dev *beep;
struct work_struct beep_work; struct work_struct beep_work;
int beep_rate; int beep_rate;
......
...@@ -807,6 +807,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { ...@@ -807,6 +807,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "IN2L PGA", NULL, "IN2L" }, { "IN2L PGA", NULL, "IN2L" },
{ "IN2R PGA", NULL, "IN2R" }, { "IN2R PGA", NULL, "IN2R" },
{ "Audio Trace DSP", NULL, "DSP2" },
{ "Audio Trace DSP", NULL, "SYSCLK" },
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
...@@ -1016,6 +1019,27 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { ...@@ -1016,6 +1019,27 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.formats = CS47L24_FORMATS, .formats = CS47L24_FORMATS,
}, },
}, },
{
.name = "cs47l24-cpu-trace",
.capture = {
.stream_name = "Audio Trace CPU",
.channels_min = 1,
.channels_max = 6,
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
.compress_new = snd_soc_new_compress,
},
{
.name = "cs47l24-dsp-trace",
.capture = {
.stream_name = "Audio Trace DSP",
.channels_min = 1,
.channels_max = 6,
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
},
}; };
static int cs47l24_open(struct snd_compr_stream *stream) static int cs47l24_open(struct snd_compr_stream *stream)
...@@ -1027,6 +1051,8 @@ static int cs47l24_open(struct snd_compr_stream *stream) ...@@ -1027,6 +1051,8 @@ static int cs47l24_open(struct snd_compr_stream *stream)
if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) { if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
n_adsp = 2; n_adsp = 2;
} else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) {
n_adsp = 1;
} else { } else {
dev_err(arizona->dev, dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n", "No suitable compressed stream for DAI '%s'\n",
...@@ -1041,10 +1067,16 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) ...@@ -1041,10 +1067,16 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{ {
struct cs47l24_priv *priv = data; struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona; struct arizona *arizona = priv->core.arizona;
int ret; int serviced = 0;
int i, ret;
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]); for (i = 1; i <= 2; ++i) {
if (ret == -ENODEV) { ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
}
if (!serviced) {
dev_err(arizona->dev, "Spurious compressed data IRQ\n"); dev_err(arizona->dev, "Spurious compressed data IRQ\n");
return IRQ_NONE; return IRQ_NONE;
} }
...@@ -1160,6 +1192,7 @@ static struct snd_compr_ops cs47l24_compr_ops = { ...@@ -1160,6 +1192,7 @@ static struct snd_compr_ops cs47l24_compr_ops = {
static struct snd_soc_platform_driver cs47l24_compr_platform = { static struct snd_soc_platform_driver cs47l24_compr_platform = {
.compr_ops = &cs47l24_compr_ops, .compr_ops = &cs47l24_compr_ops,
}; };
static int cs47l24_probe(struct platform_device *pdev) static int cs47l24_probe(struct platform_device *pdev)
{ {
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
...@@ -1228,9 +1261,9 @@ static int cs47l24_probe(struct platform_device *pdev) ...@@ -1228,9 +1261,9 @@ static int cs47l24_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
return ret; return ret;
} }
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_platform(&pdev->dev);
...@@ -1241,10 +1274,15 @@ static int cs47l24_probe(struct platform_device *pdev) ...@@ -1241,10 +1274,15 @@ static int cs47l24_probe(struct platform_device *pdev)
static int cs47l24_remove(struct platform_device *pdev) static int cs47l24_remove(struct platform_device *pdev)
{ {
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev); snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
wm_adsp2_remove(&cs47l24->core.adsp[1]);
wm_adsp2_remove(&cs47l24->core.adsp[2]);
return 0; return 0;
} }
......
...@@ -725,6 +725,68 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = { ...@@ -725,6 +725,68 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
}; };
/*
* DAPM Events
*/
static int da7213_dai_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
u8 pll_ctrl, pll_status;
int i = 0;
bool srm_lock = false;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Enable DAI clks for master mode */
if (da7213->master)
snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE,
DA7213_DAI_CLK_EN_MASK,
DA7213_DAI_CLK_EN_MASK);
/* PC synchronised to DAI */
snd_soc_update_bits(codec, DA7213_PC_COUNT,
DA7213_PC_FREERUN_MASK, 0);
/* Slave mode, if SRM not enabled no need for status checks */
pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL);
if (!(pll_ctrl & DA7213_PLL_SRM_EN))
return 0;
/* Check SRM has locked */
do {
pll_status = snd_soc_read(codec, DA7213_PLL_STATUS);
if (pll_status & DA7219_PLL_SRM_LOCK) {
srm_lock = true;
} else {
++i;
msleep(50);
}
} while ((i < DA7213_SRM_CHECK_RETRIES) & (!srm_lock));
if (!srm_lock)
dev_warn(codec->dev, "SRM failed to lock\n");
return 0;
case SND_SOC_DAPM_POST_PMD:
/* PC free-running */
snd_soc_update_bits(codec, DA7213_PC_COUNT,
DA7213_PC_FREERUN_MASK,
DA7213_PC_FREERUN_MASK);
/* Disable DAI clks if in master mode */
if (da7213->master)
snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE,
DA7213_DAI_CLK_EN_MASK, 0);
return 0;
default:
return -EINVAL;
}
}
/* /*
* DAPM widgets * DAPM widgets
*/ */
...@@ -736,7 +798,8 @@ static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = { ...@@ -736,7 +798,8 @@ static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = {
/* Use a supply here as this controls both input & output DAIs */ /* Use a supply here as this controls both input & output DAIs */
SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT, SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT,
DA7213_NO_INVERT, NULL, 0), DA7213_NO_INVERT, da7213_dai_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* /*
* Input * Input
...@@ -1143,11 +1206,9 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ...@@ -1143,11 +1206,9 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* Set master/slave mode */ /* Set master/slave mode */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE;
da7213->master = true; da7213->master = true;
break; break;
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE;
da7213->master = false; da7213->master = false;
break; break;
default: default:
...@@ -1281,28 +1342,28 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, ...@@ -1281,28 +1342,28 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_ctrl = 0; pll_ctrl = 0;
/* Workout input divider based on MCLK rate */ /* Workout input divider based on MCLK rate */
if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) { if (da7213->mclk_rate == 32768) {
/* 32KHz PLL Mode */ /* 32KHz PLL Mode */
indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
freq_ref = 3750000; freq_ref = 3750000;
pll_ctrl |= DA7213_PLL_32K_MODE; pll_ctrl |= DA7213_PLL_32K_MODE;
} else { } else {
/* 5 - 54MHz MCLK */ /* 5 - 54MHz MCLK */
if (da7213->mclk_rate < 5000000) { if (da7213->mclk_rate < 5000000) {
goto pll_err; goto pll_err;
} else if (da7213->mclk_rate <= 10000000) { } else if (da7213->mclk_rate <= 9000000) {
indiv_bits = DA7213_PLL_INDIV_5_10_MHZ; indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ;
indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL; indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL;
} else if (da7213->mclk_rate <= 20000000) { } else if (da7213->mclk_rate <= 18000000) {
indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
} else if (da7213->mclk_rate <= 40000000) { } else if (da7213->mclk_rate <= 36000000) {
indiv_bits = DA7213_PLL_INDIV_20_40_MHZ; indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ;
indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL; indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL;
} else if (da7213->mclk_rate <= 54000000) { } else if (da7213->mclk_rate <= 54000000) {
indiv_bits = DA7213_PLL_INDIV_40_54_MHZ; indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL; indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
} else { } else {
goto pll_err; goto pll_err;
} }
...@@ -1547,6 +1608,10 @@ static int da7213_probe(struct snd_soc_codec *codec) ...@@ -1547,6 +1608,10 @@ static int da7213_probe(struct snd_soc_codec *codec)
/* Default to using SRM for slave mode */ /* Default to using SRM for slave mode */
da7213->srm_en = true; da7213->srm_en = true;
/* Default PC counter to free-running */
snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
DA7213_PC_FREERUN_MASK);
/* Enable all Gain Ramps */ /* Enable all Gain Ramps */
snd_soc_update_bits(codec, DA7213_AUX_L_CTRL, snd_soc_update_bits(codec, DA7213_AUX_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
......
...@@ -142,6 +142,9 @@ ...@@ -142,6 +142,9 @@
* Bit fields * Bit fields
*/ */
/* DA7213_PLL_STATUS = 0x03 */
#define DA7219_PLL_SRM_LOCK (0x1 << 1)
/* DA7213_SR = 0x22 */ /* DA7213_SR = 0x22 */
#define DA7213_SR_8000 (0x1 << 0) #define DA7213_SR_8000 (0x1 << 0)
#define DA7213_SR_11025 (0x2 << 0) #define DA7213_SR_11025 (0x2 << 0)
...@@ -160,10 +163,10 @@ ...@@ -160,10 +163,10 @@
#define DA7213_VMID_EN (0x1 << 7) #define DA7213_VMID_EN (0x1 << 7)
/* DA7213_PLL_CTRL = 0x27 */ /* DA7213_PLL_CTRL = 0x27 */
#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2) #define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2)
#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2) #define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2)
#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2) #define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2)
#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2) #define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2)
#define DA7213_PLL_INDIV_MASK (0x3 << 2) #define DA7213_PLL_INDIV_MASK (0x3 << 2)
#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4) #define DA7213_PLL_MCLK_SQR_EN (0x1 << 4)
#define DA7213_PLL_32K_MODE (0x1 << 5) #define DA7213_PLL_32K_MODE (0x1 << 5)
...@@ -178,8 +181,6 @@ ...@@ -178,8 +181,6 @@
#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) #define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
#define DA7213_DAI_CLK_POL_INV (0x1 << 2) #define DA7213_DAI_CLK_POL_INV (0x1 << 2)
#define DA7213_DAI_WCLK_POL_INV (0x1 << 3) #define DA7213_DAI_WCLK_POL_INV (0x1 << 3)
#define DA7213_DAI_CLK_EN_SLAVE_MODE (0x0 << 7)
#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7)
#define DA7213_DAI_CLK_EN_MASK (0x1 << 7) #define DA7213_DAI_CLK_EN_MASK (0x1 << 7)
/* DA7213_DAI_CTRL = 0x29 */ /* DA7213_DAI_CTRL = 0x29 */
...@@ -412,6 +413,9 @@ ...@@ -412,6 +413,9 @@
#define DA7213_DMIC_CLK_RATE_SHIFT 2 #define DA7213_DMIC_CLK_RATE_SHIFT 2
#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2) #define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2)
/* DA7213_PC_COUNT = 0x94 */
#define DA7213_PC_FREERUN_MASK (0x1 << 0)
/* DA7213_DIG_CTRL = 0x99 */ /* DA7213_DIG_CTRL = 0x99 */
#define DA7213_DAC_L_INV_SHIFT 3 #define DA7213_DAC_L_INV_SHIFT 3
#define DA7213_DAC_R_INV_SHIFT 7 #define DA7213_DAC_R_INV_SHIFT 7
...@@ -495,15 +499,16 @@ ...@@ -495,15 +499,16 @@
#define DA7213_ALC_AVG_ITERATIONS 5 #define DA7213_ALC_AVG_ITERATIONS 5
/* PLL related */ /* PLL related */
#define DA7213_SYSCLK_MCLK 0 #define DA7213_SYSCLK_MCLK 0
#define DA7213_SYSCLK_PLL 1 #define DA7213_SYSCLK_PLL 1
#define DA7213_PLL_FREQ_OUT_90316800 90316800 #define DA7213_PLL_FREQ_OUT_90316800 90316800
#define DA7213_PLL_FREQ_OUT_98304000 98304000 #define DA7213_PLL_FREQ_OUT_98304000 98304000
#define DA7213_PLL_FREQ_OUT_94310400 94310400 #define DA7213_PLL_FREQ_OUT_94310400 94310400
#define DA7213_PLL_INDIV_5_10_MHZ_VAL 2 #define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2
#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4 #define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 #define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 #define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16
#define DA7213_SRM_CHECK_RETRIES 8
enum da7213_clk_src { enum da7213_clk_src {
DA7213_CLKSRC_MCLK = 0, DA7213_CLKSRC_MCLK = 0,
......
...@@ -1868,27 +1868,27 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, ...@@ -1868,27 +1868,27 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
if (da7218->mclk_rate == 32768) { if (da7218->mclk_rate == 32768) {
indiv_bits = DA7218_PLL_INDIV_2_5_MHZ; indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
} else if (da7218->mclk_rate < 2000000) { } else if (da7218->mclk_rate < 2000000) {
dev_err(codec->dev, "PLL input clock %d below valid range\n", dev_err(codec->dev, "PLL input clock %d below valid range\n",
da7218->mclk_rate); da7218->mclk_rate);
return -EINVAL; return -EINVAL;
} else if (da7218->mclk_rate <= 5000000) { } else if (da7218->mclk_rate <= 4500000) {
indiv_bits = DA7218_PLL_INDIV_2_5_MHZ; indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ;
indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL;
} else if (da7218->mclk_rate <= 10000000) { } else if (da7218->mclk_rate <= 9000000) {
indiv_bits = DA7218_PLL_INDIV_5_10_MHZ; indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ;
indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL;
} else if (da7218->mclk_rate <= 20000000) { } else if (da7218->mclk_rate <= 18000000) {
indiv_bits = DA7218_PLL_INDIV_10_20_MHZ; indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL; indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
} else if (da7218->mclk_rate <= 40000000) { } else if (da7218->mclk_rate <= 36000000) {
indiv_bits = DA7218_PLL_INDIV_20_40_MHZ; indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ;
indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL; indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL;
} else if (da7218->mclk_rate <= 54000000) { } else if (da7218->mclk_rate <= 54000000) {
indiv_bits = DA7218_PLL_INDIV_40_54_MHZ; indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL; indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL;
} else { } else {
dev_err(codec->dev, "PLL input clock %d above valid range\n", dev_err(codec->dev, "PLL input clock %d above valid range\n",
da7218->mclk_rate); da7218->mclk_rate);
......
...@@ -876,15 +876,11 @@ ...@@ -876,15 +876,11 @@
/* DA7218_PLL_CTRL = 0x91 */ /* DA7218_PLL_CTRL = 0x91 */
#define DA7218_PLL_INDIV_SHIFT 0 #define DA7218_PLL_INDIV_SHIFT 0
#define DA7218_PLL_INDIV_MASK (0x7 << 0) #define DA7218_PLL_INDIV_MASK (0x7 << 0)
#define DA7218_PLL_INDIV_2_5_MHZ (0x0 << 0) #define DA7218_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 0)
#define DA7218_PLL_INDIV_5_10_MHZ (0x1 << 0) #define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0)
#define DA7218_PLL_INDIV_10_20_MHZ (0x2 << 0) #define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0)
#define DA7218_PLL_INDIV_20_40_MHZ (0x3 << 0) #define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0)
#define DA7218_PLL_INDIV_40_54_MHZ (0x4 << 0) #define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0)
#define DA7218_PLL_INDIV_2_10_MHZ_VAL 2
#define DA7218_PLL_INDIV_10_20_MHZ_VAL 4
#define DA7218_PLL_INDIV_20_40_MHZ_VAL 8
#define DA7218_PLL_INDIV_40_54_MHZ_VAL 16
#define DA7218_PLL_MCLK_SQR_EN_SHIFT 4 #define DA7218_PLL_MCLK_SQR_EN_SHIFT 4
#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4) #define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4)
#define DA7218_PLL_MODE_SHIFT 6 #define DA7218_PLL_MODE_SHIFT 6
...@@ -1336,6 +1332,13 @@ ...@@ -1336,6 +1332,13 @@
#define DA7218_PLL_FREQ_OUT_90316 90316800 #define DA7218_PLL_FREQ_OUT_90316 90316800
#define DA7218_PLL_FREQ_OUT_98304 98304000 #define DA7218_PLL_FREQ_OUT_98304 98304000
/* PLL Frequency Dividers */
#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL 4
#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL 8
#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL 16
/* ALC Calibration */ /* ALC Calibration */
#define DA7218_ALC_CALIB_DELAY_MIN 2500 #define DA7218_ALC_CALIB_DELAY_MIN 2500
#define DA7218_ALC_CALIB_DELAY_MAX 5000 #define DA7218_ALC_CALIB_DELAY_MAX 5000
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* option) any later version. * option) any later version.
*/ */
#include <linux/acpi.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -1025,7 +1026,7 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, ...@@ -1025,7 +1026,7 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
return 0; return 0;
if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { if ((freq < 2000000) || (freq > 54000000)) {
dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
freq); freq);
return -EINVAL; return -EINVAL;
...@@ -1079,21 +1080,21 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, ...@@ -1079,21 +1080,21 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
dev_err(codec->dev, "PLL input clock %d below valid range\n", dev_err(codec->dev, "PLL input clock %d below valid range\n",
da7219->mclk_rate); da7219->mclk_rate);
return -EINVAL; return -EINVAL;
} else if (da7219->mclk_rate <= 5000000) { } else if (da7219->mclk_rate <= 4500000) {
indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ;
indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL;
} else if (da7219->mclk_rate <= 10000000) { } else if (da7219->mclk_rate <= 9000000) {
indiv_bits = DA7219_PLL_INDIV_5_10_MHZ; indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ;
indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL; indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL;
} else if (da7219->mclk_rate <= 20000000) { } else if (da7219->mclk_rate <= 18000000) {
indiv_bits = DA7219_PLL_INDIV_10_20_MHZ; indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ;
indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL; indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL;
} else if (da7219->mclk_rate <= 40000000) { } else if (da7219->mclk_rate <= 36000000) {
indiv_bits = DA7219_PLL_INDIV_20_40_MHZ; indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ;
indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL; indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL;
} else if (da7219->mclk_rate <= 54000000) { } else if (da7219->mclk_rate <= 54000000) {
indiv_bits = DA7219_PLL_INDIV_40_54_MHZ; indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL; indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL;
} else { } else {
dev_err(codec->dev, "PLL input clock %d above valid range\n", dev_err(codec->dev, "PLL input clock %d above valid range\n",
da7219->mclk_rate); da7219->mclk_rate);
...@@ -1426,6 +1427,12 @@ static const struct of_device_id da7219_of_match[] = { ...@@ -1426,6 +1427,12 @@ static const struct of_device_id da7219_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, da7219_of_match); MODULE_DEVICE_TABLE(of, da7219_of_match);
static const struct acpi_device_id da7219_acpi_match[] = {
{ .id = "DLGS7219", },
{ }
};
MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
static enum da7219_micbias_voltage static enum da7219_micbias_voltage
da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
{ {
...@@ -1955,6 +1962,7 @@ static struct i2c_driver da7219_i2c_driver = { ...@@ -1955,6 +1962,7 @@ static struct i2c_driver da7219_i2c_driver = {
.driver = { .driver = {
.name = "da7219", .name = "da7219",
.of_match_table = of_match_ptr(da7219_of_match), .of_match_table = of_match_ptr(da7219_of_match),
.acpi_match_table = ACPI_PTR(da7219_acpi_match),
}, },
.probe = da7219_i2c_probe, .probe = da7219_i2c_probe,
.remove = da7219_i2c_remove, .remove = da7219_i2c_remove,
......
...@@ -194,11 +194,11 @@ ...@@ -194,11 +194,11 @@
/* DA7219_PLL_CTRL = 0x20 */ /* DA7219_PLL_CTRL = 0x20 */
#define DA7219_PLL_INDIV_SHIFT 2 #define DA7219_PLL_INDIV_SHIFT 2
#define DA7219_PLL_INDIV_MASK (0x7 << 2) #define DA7219_PLL_INDIV_MASK (0x7 << 2)
#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2) #define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2)
#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2) #define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2)
#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2) #define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2)
#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2) #define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 2)
#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2) #define DA7219_PLL_INDIV_36_TO_54_MHZ (0x4 << 2)
#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 #define DA7219_PLL_MCLK_SQR_EN_SHIFT 5
#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) #define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5)
#define DA7219_PLL_MODE_SHIFT 6 #define DA7219_PLL_MODE_SHIFT 6
...@@ -761,11 +761,11 @@ ...@@ -761,11 +761,11 @@
#define DA7219_PLL_FREQ_OUT_98304 98304000 #define DA7219_PLL_FREQ_OUT_98304 98304000
/* PLL Frequency Dividers */ /* PLL Frequency Dividers */
#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1 #define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2 #define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4 #define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4
#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8 #define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8
#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16 #define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16
/* SRM */ /* SRM */
#define DA7219_SRM_CHECK_RETRIES 8 #define DA7219_SRM_CHECK_RETRIES 8
......
...@@ -26,18 +26,30 @@ ...@@ -26,18 +26,30 @@
#include <sound/tlv.h> #include <sound/tlv.h>
#include "es8328.h" #include "es8328.h"
#define ES8328_SYSCLK_RATE_1X 11289600 static const unsigned int rates_12288[] = {
#define ES8328_SYSCLK_RATE_2X 22579200 8000, 12000, 16000, 24000, 32000, 48000, 96000,
};
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */ static const int ratios_12288[] = {
static struct { 10, 7, 6, 4, 3, 2, 0,
int rate; };
u8 ratio;
} mclk_ratios[] = { static const struct snd_pcm_hw_constraint_list constraints_12288 = {
{ 8000, 9 }, .count = ARRAY_SIZE(rates_12288),
{11025, 7 }, .list = rates_12288,
{22050, 4 }, };
{44100, 2 },
static const unsigned int rates_11289[] = {
8018, 11025, 22050, 44100, 88200,
};
static const int ratios_11289[] = {
9, 7, 4, 2, 0,
};
static const struct snd_pcm_hw_constraint_list constraints_11289 = {
.count = ARRAY_SIZE(rates_11289),
.list = rates_11289,
}; };
/* regulator supplies for sgtl5000, VDDD is an optional external supply */ /* regulator supplies for sgtl5000, VDDD is an optional external supply */
...@@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = { ...@@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"HPVDD", "HPVDD",
}; };
#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ #define ES8328_RATES (SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_11025) SNDRV_PCM_RATE_16000 | \
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) SNDRV_PCM_RATE_11025 | \
SNDRV_PCM_RATE_8000)
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
struct es8328_priv { struct es8328_priv {
struct regmap *regmap; struct regmap *regmap;
struct clk *clk; struct clk *clk;
int playback_fs; int playback_fs;
bool deemph; bool deemph;
int mclkdiv2;
const struct snd_pcm_hw_constraint_list *sysclk_constraints;
const int *mclk_ratios;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
}; };
...@@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute) ...@@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
mute ? ES8328_DACCONTROL3_DACMUTE : 0); mute ? ES8328_DACCONTROL3_DACMUTE : 0);
} }
static int es8328_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
if (es8328->sysclk_constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
es8328->sysclk_constraints);
return 0;
}
static int es8328_hw_params(struct snd_pcm_substream *substream, static int es8328_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int clk_rate;
int i; int i;
int reg; int reg;
u8 ratio; int wl;
int ratio;
if (!es8328->sysclk_constraints) {
dev_err(codec->dev, "No MCLK configured\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2; reg = ES8328_DACCONTROL2;
else else
reg = ES8328_ADCCONTROL5; reg = ES8328_ADCCONTROL5;
clk_rate = clk_get_rate(es8328->clk); for (i = 0; i < es8328->sysclk_constraints->count; i++)
if (es8328->sysclk_constraints->list[i] == params_rate(params))
break;
if ((clk_rate != ES8328_SYSCLK_RATE_1X) && if (i == es8328->sysclk_constraints->count) {
(clk_rate != ES8328_SYSCLK_RATE_2X)) { dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
dev_err(codec->dev, params_rate(params));
"%s: clock is running at %d Hz, not %d or %d Hz\n",
__func__, clk_rate,
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
return -EINVAL; return -EINVAL;
} }
/* find master mode MCLK to sampling frequency ratio */ ratio = es8328->mclk_ratios[i];
ratio = mclk_ratios[0].rate; snd_soc_update_bits(codec, ES8328_MASTERMODE,
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++) ES8328_MASTERMODE_MCLKDIV2,
if (params_rate(params) <= mclk_ratios[i].rate) es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
ratio = mclk_ratios[i].ratio;
switch (params_width(params)) {
case 16:
wl = 3;
break;
case 18:
wl = 2;
break;
case 20:
wl = 1;
break;
case 24:
wl = 0;
break;
case 32:
wl = 4;
break;
default:
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_soc_update_bits(codec, ES8328_DACCONTROL1,
ES8328_DACCONTROL1_DACWL_MASK,
wl << ES8328_DACCONTROL1_DACWL_SHIFT);
es8328->playback_fs = params_rate(params); es8328->playback_fs = params_rate(params);
es8328_set_deemph(codec); es8328_set_deemph(codec);
} } else
snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
ES8328_ADCCONTROL4_ADCWL_MASK,
wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
} }
static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int mclkdiv2 = 0;
switch (freq) {
case 0:
es8328->sysclk_constraints = NULL;
es8328->mclk_ratios = NULL;
break;
case 22579200:
mclkdiv2 = 1;
/* fallthru */
case 11289600:
es8328->sysclk_constraints = &constraints_11289;
es8328->mclk_ratios = ratios_11289;
break;
case 24576000:
mclkdiv2 = 1;
/* fallthru */
case 12288000:
es8328->sysclk_constraints = &constraints_12288;
es8328->mclk_ratios = ratios_12288;
break;
default:
return -EINVAL;
}
es8328->mclkdiv2 = mclkdiv2;
return 0;
}
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt) unsigned int fmt)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); u8 dac_mode = 0;
int clk_rate; u8 adc_mode = 0;
u8 mode = ES8328_DACCONTROL1_DACWL_16;
/* set master/slave audio interface */ /* set master/slave audio interface */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
...@@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* interface format */ /* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
break; break;
case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_RIGHT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
break; break;
case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_LEFT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL; return -EINVAL;
snd_soc_write(codec, ES8328_DACCONTROL1, mode); snd_soc_update_bits(codec, ES8328_DACCONTROL1,
snd_soc_write(codec, ES8328_ADCCONTROL4, mode); ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
/* Master serial port mode, with BCLK generated automatically */ /* Master serial port mode, with BCLK generated automatically */
clk_rate = clk_get_rate(es8328->clk); snd_soc_update_bits(codec, ES8328_MASTERMODE,
if (clk_rate == ES8328_SYSCLK_RATE_1X) ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC);
else
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MCLKDIV2 |
ES8328_MASTERMODE_MSC);
return 0; return 0;
} }
...@@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, ...@@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
} }
static const struct snd_soc_dai_ops es8328_dai_ops = { static const struct snd_soc_dai_ops es8328_dai_ops = {
.startup = es8328_startup,
.hw_params = es8328_hw_params, .hw_params = es8328_hw_params,
.digital_mute = es8328_mute, .digital_mute = es8328_mute,
.set_sysclk = es8328_set_sysclk,
.set_fmt = es8328_set_dai_fmt, .set_fmt = es8328_set_dai_fmt,
}; };
...@@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = { ...@@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = {
.formats = ES8328_FORMATS, .formats = ES8328_FORMATS,
}, },
.ops = &es8328_dai_ops, .ops = &es8328_dai_ops,
.symmetric_rates = 1,
}; };
static int es8328_suspend(struct snd_soc_codec *codec) static int es8328_suspend(struct snd_soc_codec *codec)
...@@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = { ...@@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = {
.val_bits = 8, .val_bits = 8,
.max_register = ES8328_REG_MAX, .max_register = ES8328_REG_MAX,
.cache_type = REGCACHE_RBTREE, .cache_type = REGCACHE_RBTREE,
.use_single_rw = true,
}; };
EXPORT_SYMBOL_GPL(es8328_regmap_config); EXPORT_SYMBOL_GPL(es8328_regmap_config);
......
...@@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); ...@@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0) #define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
#define ES8328_CONTROL1_ENREF (1 << 2) #define ES8328_CONTROL1_ENREF (1 << 2)
#define ES8328_CONTROL1_SEQEN (1 << 3) #define ES8328_CONTROL1_SEQEN (1 << 3)
#define ES8328_CONTROL1_SAMEFS (1 << 4) #define ES8328_CONTROL1_SAMEFS (1 << 4)
...@@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap); ...@@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_ADCCONTROL1 0x09 #define ES8328_ADCCONTROL1 0x09
#define ES8328_ADCCONTROL2 0x0a #define ES8328_ADCCONTROL2 0x0a
#define ES8328_ADCCONTROL3 0x0b #define ES8328_ADCCONTROL3 0x0b
#define ES8328_ADCCONTROL4 0x0c #define ES8328_ADCCONTROL4 0x0c
#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
#define ES8328_ADCCONTROL5 0x0d #define ES8328_ADCCONTROL5 0x0d
#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
...@@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap); ...@@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_ADCCONTROL14 0x16 #define ES8328_ADCCONTROL14 0x16
#define ES8328_DACCONTROL1 0x17 #define ES8328_DACCONTROL1 0x17
#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3) #define ES8328_DACCONTROL1_DACWL_SHIFT 3
#define ES8328_DACCONTROL1_DACWL_20 (1 << 3) #define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <sound/hdaudio_ext.h> #include <sound/hdaudio_ext.h>
#include <sound/hda_i915.h> #include <sound/hda_i915.h>
#include <sound/pcm_drm_eld.h> #include <sound/pcm_drm_eld.h>
#include <sound/hda_chmap.h>
#include "../../hda/local.h" #include "../../hda/local.h"
#include "hdac_hdmi.h" #include "hdac_hdmi.h"
...@@ -60,11 +61,17 @@ struct hdac_hdmi_cvt { ...@@ -60,11 +61,17 @@ struct hdac_hdmi_cvt {
struct hdac_hdmi_cvt_params params; struct hdac_hdmi_cvt_params params;
}; };
/* Currently only spk_alloc, more to be added */
struct hdac_hdmi_parsed_eld {
u8 spk_alloc;
};
struct hdac_hdmi_eld { struct hdac_hdmi_eld {
bool monitor_present; bool monitor_present;
bool eld_valid; bool eld_valid;
int eld_size; int eld_size;
char eld_buffer[ELD_MAX_SIZE]; char eld_buffer[ELD_MAX_SIZE];
struct hdac_hdmi_parsed_eld info;
}; };
struct hdac_hdmi_pin { struct hdac_hdmi_pin {
...@@ -76,6 +83,10 @@ struct hdac_hdmi_pin { ...@@ -76,6 +83,10 @@ struct hdac_hdmi_pin {
struct hdac_ext_device *edev; struct hdac_ext_device *edev;
int repoll_count; int repoll_count;
struct delayed_work work; struct delayed_work work;
struct mutex lock;
bool chmap_set;
unsigned char chmap[8]; /* ALSA API channel-map */
int channels; /* current number of channels */
}; };
struct hdac_hdmi_pcm { struct hdac_hdmi_pcm {
...@@ -100,8 +111,22 @@ struct hdac_hdmi_priv { ...@@ -100,8 +111,22 @@ struct hdac_hdmi_priv {
int num_pin; int num_pin;
int num_cvt; int num_cvt;
struct mutex pin_mutex; struct mutex pin_mutex;
struct hdac_chmap chmap;
}; };
static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
int pcm_idx)
{
struct hdac_hdmi_pcm *pcm;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->pcm_id == pcm_idx)
return pcm;
}
return NULL;
}
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
{ {
struct hdac_device *hdac = dev_to_hdac_dev(dev); struct hdac_device *hdac = dev_to_hdac_dev(dev);
...@@ -278,26 +303,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, ...@@ -278,26 +303,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
int i; int i;
const u8 *eld_buf; const u8 *eld_buf;
u8 conn_type; u8 conn_type;
int channels = 2; int channels, ca;
list_for_each_entry(pin, &hdmi->pin_list, head) { list_for_each_entry(pin, &hdmi->pin_list, head) {
if (pin->nid == pin_nid) if (pin->nid == pin_nid)
break; break;
} }
ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc,
pin->channels, pin->chmap_set, true, pin->chmap);
channels = snd_hdac_get_active_channels(ca);
hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels);
snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
pin->channels, pin->chmap, pin->chmap_set);
eld_buf = pin->eld.eld_buffer; eld_buf = pin->eld.eld_buffer;
conn_type = drm_eld_get_conn_type(eld_buf); conn_type = drm_eld_get_conn_type(eld_buf);
/* setup channel count */
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
switch (conn_type) { switch (conn_type) {
case DRM_ELD_CONN_TYPE_HDMI: case DRM_ELD_CONN_TYPE_HDMI:
hdmi_audio_infoframe_init(&frame); hdmi_audio_infoframe_init(&frame);
/* Default stereo for now */
frame.channels = channels; frame.channels = channels;
frame.channel_allocation = ca;
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (ret < 0) if (ret < 0)
...@@ -311,7 +341,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, ...@@ -311,7 +341,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
dp_ai.len = 0x1b; dp_ai.len = 0x1b;
dp_ai.ver = 0x11 << 2; dp_ai.ver = 0x11 << 2;
dp_ai.CC02_CT47 = channels - 1; dp_ai.CC02_CT47 = channels - 1;
dp_ai.CA = 0; dp_ai.CA = ca;
dip = (u8 *)&dp_ai; dip = (u8 *)&dp_ai;
break; break;
...@@ -370,17 +400,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, ...@@ -370,17 +400,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_priv *hdmi = hdac->private_data;
struct hdac_hdmi_dai_pin_map *dai_map; struct hdac_hdmi_dai_pin_map *dai_map;
struct hdac_hdmi_pin *pin;
struct hdac_ext_dma_params *dd; struct hdac_ext_dma_params *dd;
int ret; int ret;
dai_map = &hdmi->dai_map[dai->id]; dai_map = &hdmi->dai_map[dai->id];
pin = dai_map->pin;
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n", dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
dd->stream_tag, dd->format); dd->stream_tag, dd->format);
mutex_lock(&pin->lock);
pin->channels = substream->runtime->channels;
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
dai_map->pin->nid); dai_map->pin->nid);
mutex_unlock(&pin->lock);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -640,6 +676,12 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, ...@@ -640,6 +676,12 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
mutex_lock(&dai_map->pin->lock);
dai_map->pin->chmap_set = false;
memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap));
dai_map->pin->channels = 0;
mutex_unlock(&dai_map->pin->lock);
dai_map->pin = NULL; dai_map->pin = NULL;
} }
} }
...@@ -647,10 +689,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, ...@@ -647,10 +689,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
static int static int
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
{ {
unsigned int chans;
struct hdac_ext_device *edev = to_ehdac_device(hdac);
struct hdac_hdmi_priv *hdmi = edev->private_data;
int err; int err;
/* Only stereo supported as of now */ chans = get_wcaps(hdac, cvt->nid);
cvt->params.channels_min = cvt->params.channels_max = 2; chans = get_wcaps_channels(chans);
cvt->params.channels_min = 2;
cvt->params.channels_max = chans;
if (chans > hdmi->chmap.channels_max)
hdmi->chmap.channels_max = chans;
err = snd_hdac_query_supported_pcm(hdac, cvt->nid, err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
&cvt->params.rates, &cvt->params.rates,
...@@ -1008,6 +1059,12 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) ...@@ -1008,6 +1059,12 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
} }
static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
struct hdac_hdmi_pin *pin)
{
pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER];
}
static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
{ {
struct hdac_ext_device *edev = pin->edev; struct hdac_ext_device *edev = pin->edev;
...@@ -1065,6 +1122,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) ...@@ -1065,6 +1122,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
snd_jack_report(pcm->jack, SND_JACK_AVOUT); snd_jack_report(pcm->jack, SND_JACK_AVOUT);
} }
hdac_hdmi_parse_eld(edev, pin);
print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
pin->eld.eld_buffer, pin->eld.eld_size); pin->eld.eld_buffer, pin->eld.eld_size);
...@@ -1123,6 +1181,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) ...@@ -1123,6 +1181,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
hdmi->num_pin++; hdmi->num_pin++;
pin->edev = edev; pin->edev = edev;
mutex_init(&pin->lock);
INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
return 0; return 0;
...@@ -1342,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = { ...@@ -1342,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = {
.pin_eld_notify = hdac_hdmi_eld_notify_cb, .pin_eld_notify = hdac_hdmi_eld_notify_cb,
}; };
static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
int device)
{
struct snd_soc_pcm_runtime *rtd;
list_for_each_entry(rtd, &card->rtd_list, list) {
if (rtd->pcm && (rtd->pcm->device == device))
return rtd->pcm;
}
return NULL;
}
int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
{ {
char jack_name[NAME_SIZE]; char jack_name[NAME_SIZE];
...@@ -1351,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) ...@@ -1351,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
snd_soc_component_get_dapm(&codec->component); snd_soc_component_get_dapm(&codec->component);
struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm; struct hdac_hdmi_pcm *pcm;
struct snd_pcm *snd_pcm;
int err;
/* /*
* this is a new PCM device, create new pcm and * this is a new PCM device, create new pcm and
...@@ -1362,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) ...@@ -1362,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
pcm->pcm_id = device; pcm->pcm_id = device;
pcm->cvt = hdmi->dai_map[dai->id].cvt; pcm->cvt = hdmi->dai_map[dai->id].cvt;
snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
if (snd_pcm) {
err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
if (err < 0) {
dev_err(&edev->hdac.dev,
"chmap control add failed with err: %d for pcm: %d\n",
err, device);
kfree(pcm);
return err;
}
}
list_add_tail(&pcm->head, &hdmi->pcm_list); list_add_tail(&pcm->head, &hdmi->pcm_list);
sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device); sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
...@@ -1378,10 +1464,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) ...@@ -1378,10 +1464,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
struct snd_soc_dapm_context *dapm = struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(&codec->component); snd_soc_component_get_dapm(&codec->component);
struct hdac_hdmi_pin *pin; struct hdac_hdmi_pin *pin;
struct hdac_ext_link *hlink = NULL;
int ret; int ret;
edev->scodec = codec; edev->scodec = codec;
/*
* hold the ref while we probe, also no need to drop the ref on
* exit, we call pm_runtime_suspend() so that will do for us
*/
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
ret = create_fill_widget_route_map(dapm); ret = create_fill_widget_route_map(dapm);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1475,19 +1569,83 @@ static struct snd_soc_codec_driver hdmi_hda_codec = { ...@@ -1475,19 +1569,83 @@ static struct snd_soc_codec_driver hdmi_hda_codec = {
.idle_bias_off = true, .idle_bias_off = true,
}; };
static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap)
{
struct hdac_ext_device *edev = to_ehdac_device(hdac);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
struct hdac_hdmi_pin *pin = pcm->pin;
/* chmap is already set to 0 in caller */
if (!pin)
return;
memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap));
}
static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap, int prepared)
{
struct hdac_ext_device *edev = to_ehdac_device(hdac);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
struct hdac_hdmi_pin *pin = pcm->pin;
mutex_lock(&pin->lock);
pin->chmap_set = true;
memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap));
if (prepared)
hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid);
mutex_unlock(&pin->lock);
}
static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
{
struct hdac_ext_device *edev = to_ehdac_device(hdac);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
struct hdac_hdmi_pin *pin = pcm->pin;
return pin ? true:false;
}
static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
{
struct hdac_ext_device *edev = to_ehdac_device(hdac);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
struct hdac_hdmi_pin *pin = pcm->pin;
if (!pin || !pin->eld.eld_valid)
return 0;
return pin->eld.info.spk_alloc;
}
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
{ {
struct hdac_device *codec = &edev->hdac; struct hdac_device *codec = &edev->hdac;
struct hdac_hdmi_priv *hdmi_priv; struct hdac_hdmi_priv *hdmi_priv;
struct snd_soc_dai_driver *hdmi_dais = NULL; struct snd_soc_dai_driver *hdmi_dais = NULL;
struct hdac_ext_link *hlink = NULL;
int num_dais = 0; int num_dais = 0;
int ret = 0; int ret = 0;
/* hold the ref while we probe */
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
if (hdmi_priv == NULL) if (hdmi_priv == NULL)
return -ENOMEM; return -ENOMEM;
edev->private_data = hdmi_priv; edev->private_data = hdmi_priv;
snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap);
hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
dev_set_drvdata(&codec->dev, edev); dev_set_drvdata(&codec->dev, edev);
...@@ -1516,8 +1674,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) ...@@ -1516,8 +1674,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
} }
/* ASoC specific initialization */ /* ASoC specific initialization */
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
hdmi_dais, num_dais); hdmi_dais, num_dais);
snd_hdac_ext_bus_link_put(edev->ebus, hlink);
return ret;
} }
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
...@@ -1556,6 +1718,8 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) ...@@ -1556,6 +1718,8 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_ext_device *edev = to_hda_ext_device(dev);
struct hdac_device *hdac = &edev->hdac; struct hdac_device *hdac = &edev->hdac;
struct hdac_bus *bus = hdac->bus; struct hdac_bus *bus = hdac->bus;
struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
struct hdac_ext_link *hlink = NULL;
int err; int err;
dev_dbg(dev, "Enter: %s\n", __func__); dev_dbg(dev, "Enter: %s\n", __func__);
...@@ -1579,6 +1743,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) ...@@ -1579,6 +1743,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
return err; return err;
} }
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
snd_hdac_ext_bus_link_put(ebus, hlink);
return 0; return 0;
} }
...@@ -1587,6 +1754,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev) ...@@ -1587,6 +1754,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_ext_device *edev = to_hda_ext_device(dev);
struct hdac_device *hdac = &edev->hdac; struct hdac_device *hdac = &edev->hdac;
struct hdac_bus *bus = hdac->bus; struct hdac_bus *bus = hdac->bus;
struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
struct hdac_ext_link *hlink = NULL;
int err; int err;
dev_dbg(dev, "Enter: %s\n", __func__); dev_dbg(dev, "Enter: %s\n", __func__);
...@@ -1595,6 +1764,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev) ...@@ -1595,6 +1764,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
if (!bus) if (!bus)
return 0; return 0;
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
snd_hdac_ext_bus_link_get(ebus, hlink);
err = snd_hdac_display_power(bus, true); err = snd_hdac_display_power(bus, true);
if (err < 0) { if (err < 0) {
dev_err(bus->dev, "Cannot turn on display power on i915\n"); dev_err(bus->dev, "Cannot turn on display power on i915\n");
......
/*
* ALSA SoC codec for HDMI encoder drivers
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Author: Jyri Sarha <jsarha@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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.
*/
#include <linux/module.h>
#include <linux/string.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/pcm_drm_eld.h>
#include <sound/hdmi-codec.h>
#include <sound/pcm_iec958.h>
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
struct hdmi_codec_priv {
struct hdmi_codec_pdata hcd;
struct snd_soc_dai_driver *daidrv;
struct hdmi_codec_daifmt daifmt[2];
struct mutex current_stream_lock;
struct snd_pcm_substream *current_stream;
struct snd_pcm_hw_constraint_list ratec;
uint8_t eld[MAX_ELD_BYTES];
};
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
SND_SOC_DAPM_OUTPUT("TX"),
};
static const struct snd_soc_dapm_route hdmi_routes[] = {
{ "TX", NULL, "Playback" },
};
enum {
DAI_ID_I2S = 0,
DAI_ID_SPDIF,
};
static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = sizeof(hcp->eld);
return 0;
}
static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld));
return 0;
}
static const struct snd_kcontrol_new hdmi_controls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
.get = hdmi_eld_ctl_get,
},
};
static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
int ret = 0;
mutex_lock(&hcp->current_stream_lock);
if (!hcp->current_stream) {
hcp->current_stream = substream;
} else if (hcp->current_stream != substream) {
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
ret = -EINVAL;
}
mutex_unlock(&hcp->current_stream_lock);
return ret;
}
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
int ret = 0;
dev_dbg(dai->dev, "%s()\n", __func__);
ret = hdmi_codec_new_stream(substream, dai);
if (ret)
return ret;
if (hcp->hcd.ops->audio_startup) {
ret = hcp->hcd.ops->audio_startup(dai->dev->parent);
if (ret) {
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
mutex_unlock(&hcp->current_stream_lock);
return ret;
}
}
if (hcp->hcd.ops->get_eld) {
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld,
sizeof(hcp->eld));
if (!ret) {
ret = snd_pcm_hw_constraint_eld(substream->runtime,
hcp->eld);
if (ret)
return ret;
}
}
return 0;
}
static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
dev_dbg(dai->dev, "%s()\n", __func__);
WARN_ON(hcp->current_stream != substream);
hcp->hcd.ops->audio_shutdown(dai->dev->parent);
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
mutex_unlock(&hcp->current_stream_lock);
}
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct hdmi_codec_params hp = {
.iec = {
.status = { 0 },
.subcode = { 0 },
.pad = 0,
.dig_subframe = { 0 },
}
};
int ret;
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
params_width(params), params_rate(params),
params_channels(params));
if (params_width(params) > 24)
params->msbits = 24;
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
sizeof(hp.iec.status));
if (ret < 0) {
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
ret);
return ret;
}
ret = hdmi_codec_new_stream(substream, dai);
if (ret)
return ret;
hdmi_audio_infoframe_init(&hp.cea);
hp.cea.channels = params_channels(params);
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
hp.sample_width = params_width(params);
hp.sample_rate = params_rate(params);
hp.channels = params_channels(params);
return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id],
&hp);
}
static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct hdmi_codec_daifmt cf = { 0 };
int ret = 0;
dev_dbg(dai->dev, "%s()\n", __func__);
if (dai->id == DAI_ID_SPDIF) {
cf.fmt = HDMI_SPDIF;
} else {
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
cf.bit_clk_master = 1;
cf.frame_clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFM:
cf.frame_clk_master = 1;
break;
case SND_SOC_DAIFMT_CBM_CFS:
cf.bit_clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
cf.frame_clk_inv = 1;
break;
case SND_SOC_DAIFMT_IB_NF:
cf.bit_clk_inv = 1;
break;
case SND_SOC_DAIFMT_IB_IF:
cf.frame_clk_inv = 1;
cf.bit_clk_inv = 1;
break;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
cf.fmt = HDMI_I2S;
break;
case SND_SOC_DAIFMT_DSP_A:
cf.fmt = HDMI_DSP_A;
break;
case SND_SOC_DAIFMT_DSP_B:
cf.fmt = HDMI_DSP_B;
break;
case SND_SOC_DAIFMT_RIGHT_J:
cf.fmt = HDMI_RIGHT_J;
break;
case SND_SOC_DAIFMT_LEFT_J:
cf.fmt = HDMI_LEFT_J;
break;
case SND_SOC_DAIFMT_AC97:
cf.fmt = HDMI_AC97;
break;
default:
dev_err(dai->dev, "Invalid DAI interface format\n");
return -EINVAL;
}
}
hcp->daifmt[dai->id] = cf;
return ret;
}
static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
dev_dbg(dai->dev, "%s()\n", __func__);
if (hcp->hcd.ops->digital_mute)
return hcp->hcd.ops->digital_mute(dai->dev->parent, mute);
return 0;
}
static const struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdmi_codec_startup,
.shutdown = hdmi_codec_shutdown,
.hw_params = hdmi_codec_hw_params,
.set_fmt = hdmi_codec_set_fmt,
.digital_mute = hdmi_codec_digital_mute,
};
#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
SNDRV_PCM_RATE_192000)
#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
/*
* This list is only for formats allowed on the I2S bus. So there is
* some formats listed that are not supported by HDMI interface. For
* instance allowing the 32-bit formats enables 24-precision with CPU
* DAIs that do not support 24-bit formats. If the extra formats cause
* problems, we should add the video side driver an option to disable
* them.
*/
#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
static struct snd_soc_dai_driver hdmi_i2s_dai = {
.name = "i2s-hifi",
.id = DAI_ID_I2S,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = HDMI_RATES,
.formats = I2S_FORMATS,
.sig_bits = 24,
},
.ops = &hdmi_dai_ops,
};
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.name = "spdif-hifi",
.id = DAI_ID_SPDIF,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = HDMI_RATES,
.formats = SPDIF_FORMATS,
},
.ops = &hdmi_dai_ops,
};
static struct snd_soc_codec_driver hdmi_codec = {
.controls = hdmi_controls,
.num_controls = ARRAY_SIZE(hdmi_controls),
.dapm_widgets = hdmi_widgets,
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
.dapm_routes = hdmi_routes,
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
};
static int hdmi_codec_probe(struct platform_device *pdev)
{
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct hdmi_codec_priv *hcp;
int dai_count, i = 0;
int ret;
dev_dbg(dev, "%s()\n", __func__);
if (!hcd) {
dev_err(dev, "%s: No plalform data\n", __func__);
return -EINVAL;
}
dai_count = hcd->i2s + hcd->spdif;
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
!hcd->ops->audio_shutdown) {
dev_err(dev, "%s: Invalid parameters\n", __func__);
return -EINVAL;
}
hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
if (!hcp)
return -ENOMEM;
hcp->hcd = *hcd;
mutex_init(&hcp->current_stream_lock);
hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
GFP_KERNEL);
if (!hcp->daidrv)
return -ENOMEM;
if (hcd->i2s) {
hcp->daidrv[i] = hdmi_i2s_dai;
hcp->daidrv[i].playback.channels_max =
hcd->max_i2s_channels;
i++;
}
if (hcd->spdif)
hcp->daidrv[i] = hdmi_spdif_dai;
ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
dai_count);
if (ret) {
dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n",
__func__, ret);
return ret;
}
dev_set_drvdata(dev, hcp);
return 0;
}
static int hdmi_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver hdmi_codec_driver = {
.driver = {
.name = HDMI_CODEC_DRV_NAME,
},
.probe = hdmi_codec_probe,
.remove = hdmi_codec_remove,
};
module_platform_driver(hdmi_codec_driver);
MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
MODULE_DESCRIPTION("HDMI Audio Codec Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
/*
* Driver for the PCM5102A codec
*
* Author: Florian Meier <florian.meier@koalo.de>
* Copyright 2013
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
static struct snd_soc_dai_driver pcm5102a_dai = {
.name = "pcm5102a-hifi",
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE
},
};
static struct snd_soc_codec_driver soc_codec_dev_pcm5102a;
static int pcm5102a_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a,
&pcm5102a_dai, 1);
}
static int pcm5102a_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static const struct of_device_id pcm5102a_of_match[] = {
{ .compatible = "ti,pcm5102a", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm5102a_of_match);
static struct platform_driver pcm5102a_codec_driver = {
.probe = pcm5102a_probe,
.remove = pcm5102a_remove,
.driver = {
.name = "pcm5102a-codec",
.owner = THIS_MODULE,
.of_match_table = pcm5102a_of_match,
},
};
module_platform_driver(pcm5102a_codec_driver);
MODULE_DESCRIPTION("ASoC PCM5102A codec driver");
MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
MODULE_LICENSE("GPL v2");
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/dmi.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
...@@ -1132,6 +1133,17 @@ static const struct acpi_device_id rt298_acpi_match[] = { ...@@ -1132,6 +1133,17 @@ static const struct acpi_device_id rt298_acpi_match[] = {
}; };
MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); MODULE_DEVICE_TABLE(acpi, rt298_acpi_match);
static const struct dmi_system_id force_combo_jack_table[] = {
{
.ident = "Intel Broxton P",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P")
}
},
{ }
};
static int rt298_i2c_probe(struct i2c_client *i2c, static int rt298_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -1184,11 +1196,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c, ...@@ -1184,11 +1196,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
/* enable jack combo mode on supported devices */ /* enable jack combo mode on supported devices */
acpiid = acpi_match_device(dev->driver->acpi_match_table, dev); acpiid = acpi_match_device(dev->driver->acpi_match_table, dev);
if (acpiid) { if (acpiid && acpiid->driver_data) {
rt298->pdata = *(struct rt298_platform_data *) rt298->pdata = *(struct rt298_platform_data *)
acpiid->driver_data; acpiid->driver_data;
} }
if (dmi_check_system(force_combo_jack_table)) {
rt298->pdata.cbj_en = true;
rt298->pdata.gpio2_en = false;
}
/* VREF Charging */ /* VREF Charging */
regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80); regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80);
regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860); regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860);
......
...@@ -3286,10 +3286,8 @@ static void rt5645_jack_detect_work(struct work_struct *work) ...@@ -3286,10 +3286,8 @@ static void rt5645_jack_detect_work(struct work_struct *work)
if (btn_type == 0)/* button release */ if (btn_type == 0)/* button release */
report = rt5645->jack_type; report = rt5645->jack_type;
else { else {
if (rt5645->pdata.jd_invert) { mod_timer(&rt5645->btn_check_timer,
mod_timer(&rt5645->btn_check_timer, msecs_to_jiffies(100));
msecs_to_jiffies(100));
}
} }
break; break;
...@@ -3557,6 +3555,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { ...@@ -3557,6 +3555,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
}, },
}, },
{
.ident = "Google Setzer",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
},
},
{ } { }
}; };
...@@ -3810,9 +3814,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ...@@ -3810,9 +3814,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (rt5645->pdata.jd_invert) { if (rt5645->pdata.jd_invert) {
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
setup_timer(&rt5645->btn_check_timer,
rt5645_btn_check_callback, (unsigned long)rt5645);
} }
setup_timer(&rt5645->btn_check_timer,
rt5645_btn_check_callback, (unsigned long)rt5645);
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
......
...@@ -2098,10 +2098,14 @@ static int wm5102_probe(struct platform_device *pdev) ...@@ -2098,10 +2098,14 @@ static int wm5102_probe(struct platform_device *pdev)
static int wm5102_remove(struct platform_device *pdev) static int wm5102_remove(struct platform_device *pdev)
{ {
struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev); snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
wm_adsp2_remove(&wm5102->core.adsp[0]);
return 0; return 0;
} }
......
...@@ -2437,10 +2437,16 @@ static int wm5110_probe(struct platform_device *pdev) ...@@ -2437,10 +2437,16 @@ static int wm5110_probe(struct platform_device *pdev)
static int wm5110_remove(struct platform_device *pdev) static int wm5110_remove(struct platform_device *pdev)
{ {
struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
int i;
snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev); snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
for (i = 0; i < WM5110_NUM_ADSP; i++)
wm_adsp2_remove(&wm5110->core.adsp[i]);
return 0; return 0;
} }
......
...@@ -160,6 +160,8 @@ ...@@ -160,6 +160,8 @@
#define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1 #define ADSP2_RAM_RDY_WIDTH 1
#define ADSP_MAX_STD_CTRL_SIZE 512
struct wm_adsp_buf { struct wm_adsp_buf {
struct list_head list; struct list_head list;
void *buf; void *buf;
...@@ -271,8 +273,11 @@ struct wm_adsp_buffer { ...@@ -271,8 +273,11 @@ struct wm_adsp_buffer {
__be32 words_written[2]; /* total words written (64 bit) */ __be32 words_written[2]; /* total words written (64 bit) */
}; };
struct wm_adsp_compr;
struct wm_adsp_compr_buf { struct wm_adsp_compr_buf {
struct wm_adsp *dsp; struct wm_adsp *dsp;
struct wm_adsp_compr *compr;
struct wm_adsp_buffer_region *regions; struct wm_adsp_buffer_region *regions;
u32 host_buf_ptr; u32 host_buf_ptr;
...@@ -435,6 +440,7 @@ struct wm_coeff_ctl { ...@@ -435,6 +440,7 @@ struct wm_coeff_ctl {
size_t len; size_t len;
unsigned int set:1; unsigned int set:1;
struct snd_kcontrol *kcontrol; struct snd_kcontrol *kcontrol;
struct soc_bytes_ext bytes_ext;
unsigned int flags; unsigned int flags;
}; };
...@@ -711,10 +717,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) ...@@ -711,10 +717,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
be16_to_cpu(scratch[3])); be16_to_cpu(scratch[3]));
} }
static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
{
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
}
static int wm_coeff_info(struct snd_kcontrol *kctl, static int wm_coeff_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = ctl->len; uinfo->count = ctl->len;
...@@ -763,7 +776,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, ...@@ -763,7 +776,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
static int wm_coeff_put(struct snd_kcontrol *kctl, static int wm_coeff_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
char *p = ucontrol->value.bytes.data; char *p = ucontrol->value.bytes.data;
int ret = 0; int ret = 0;
...@@ -780,6 +795,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, ...@@ -780,6 +795,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
return ret; return ret;
} }
static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
const unsigned int __user *bytes, unsigned int size)
{
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
int ret = 0;
mutex_lock(&ctl->dsp->pwr_lock);
if (copy_from_user(ctl->cache, bytes, size)) {
ret = -EFAULT;
} else {
ctl->set = 1;
if (ctl->enabled)
ret = wm_coeff_write_control(ctl, ctl->cache, size);
}
mutex_unlock(&ctl->dsp->pwr_lock);
return ret;
}
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
void *buf, size_t len) void *buf, size_t len)
{ {
...@@ -822,7 +860,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, ...@@ -822,7 +860,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
static int wm_coeff_get(struct snd_kcontrol *kctl, static int wm_coeff_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
char *p = ucontrol->value.bytes.data; char *p = ucontrol->value.bytes.data;
int ret = 0; int ret = 0;
...@@ -845,12 +885,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, ...@@ -845,12 +885,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
return ret; return ret;
} }
static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
unsigned int __user *bytes, unsigned int size)
{
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
int ret = 0;
mutex_lock(&ctl->dsp->pwr_lock);
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
if (ctl->enabled)
ret = wm_coeff_read_control(ctl, ctl->cache, size);
else
ret = -EPERM;
} else {
if (!ctl->flags && ctl->enabled)
ret = wm_coeff_read_control(ctl, ctl->cache, size);
}
if (!ret && copy_to_user(bytes, ctl->cache, size))
ret = -EFAULT;
mutex_unlock(&ctl->dsp->pwr_lock);
return ret;
}
struct wmfw_ctl_work { struct wmfw_ctl_work {
struct wm_adsp *dsp; struct wm_adsp *dsp;
struct wm_coeff_ctl *ctl; struct wm_coeff_ctl *ctl;
struct work_struct work; struct work_struct work;
}; };
static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
{
unsigned int out, rd, wr, vol;
if (len > ADSP_MAX_STD_CTRL_SIZE) {
rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
} else {
rd = SNDRV_CTL_ELEM_ACCESS_READ;
wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
out = 0;
}
if (in) {
if (in & WMFW_CTL_FLAG_READABLE)
out |= rd;
if (in & WMFW_CTL_FLAG_WRITEABLE)
out |= wr;
if (in & WMFW_CTL_FLAG_VOLATILE)
out |= vol;
} else {
out |= rd | wr | vol;
}
return out;
}
static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
{ {
struct snd_kcontrol_new *kcontrol; struct snd_kcontrol_new *kcontrol;
...@@ -868,19 +968,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) ...@@ -868,19 +968,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol->info = wm_coeff_info; kcontrol->info = wm_coeff_info;
kcontrol->get = wm_coeff_get; kcontrol->get = wm_coeff_get;
kcontrol->put = wm_coeff_put; kcontrol->put = wm_coeff_put;
kcontrol->private_value = (unsigned long)ctl; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
if (ctl->flags) { ctl->bytes_ext.max = ctl->len;
if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) ctl->bytes_ext.get = wm_coeff_tlv_get;
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; ctl->bytes_ext.put = wm_coeff_tlv_put;
if (ctl->flags & WMFW_CTL_FLAG_READABLE)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
} else {
kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
}
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
if (ret < 0) if (ret < 0)
...@@ -944,6 +1040,13 @@ static void wm_adsp_ctl_work(struct work_struct *work) ...@@ -944,6 +1040,13 @@ static void wm_adsp_ctl_work(struct work_struct *work)
kfree(ctl_work); kfree(ctl_work);
} }
static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
{
kfree(ctl->cache);
kfree(ctl->name);
kfree(ctl);
}
static int wm_adsp_create_control(struct wm_adsp *dsp, static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region, const struct wm_adsp_alg_region *alg_region,
unsigned int offset, unsigned int len, unsigned int offset, unsigned int len,
...@@ -1032,11 +1135,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ...@@ -1032,11 +1135,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ctl->flags = flags; ctl->flags = flags;
ctl->offset = offset; ctl->offset = offset;
if (len > 512) {
adsp_warn(dsp, "Truncating control %s from %d\n",
ctl->name, len);
len = 512;
}
ctl->len = len; ctl->len = len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL); ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) { if (!ctl->cache) {
...@@ -1564,6 +1662,19 @@ static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, ...@@ -1564,6 +1662,19 @@ static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
return alg_region; return alg_region;
} }
static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
{
struct wm_adsp_alg_region *alg_region;
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions,
struct wm_adsp_alg_region,
list);
list_del(&alg_region->list);
kfree(alg_region);
}
}
static int wm_adsp1_setup_algs(struct wm_adsp *dsp) static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
{ {
struct wmfw_adsp1_id_hdr adsp1_id; struct wmfw_adsp1_id_hdr adsp1_id;
...@@ -1994,7 +2105,6 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, ...@@ -1994,7 +2105,6 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift]; struct wm_adsp *dsp = &dsps[w->shift];
struct wm_adsp_alg_region *alg_region;
struct wm_coeff_ctl *ctl; struct wm_coeff_ctl *ctl;
int ret; int ret;
unsigned int val; unsigned int val;
...@@ -2074,13 +2184,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, ...@@ -2074,13 +2184,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
list_for_each_entry(ctl, &dsp->ctl_list, list) list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0; ctl->enabled = 0;
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions, wm_adsp_free_alg_regions(dsp);
struct wm_adsp_alg_region,
list);
list_del(&alg_region->list);
kfree(alg_region);
}
break; break;
default: default:
...@@ -2222,7 +2327,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ...@@ -2222,7 +2327,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift]; struct wm_adsp *dsp = &dsps[w->shift];
struct wm_adsp_alg_region *alg_region;
struct wm_coeff_ctl *ctl; struct wm_coeff_ctl *ctl;
int ret; int ret;
...@@ -2240,9 +2344,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ...@@ -2240,9 +2344,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0) if (ret != 0)
goto err; goto err;
mutex_lock(&dsp->pwr_lock);
if (wm_adsp_fw[dsp->fw].num_caps != 0) if (wm_adsp_fw[dsp->fw].num_caps != 0)
ret = wm_adsp_buffer_init(dsp); ret = wm_adsp_buffer_init(dsp);
mutex_unlock(&dsp->pwr_lock);
break; break;
case SND_SOC_DAPM_PRE_PMD: case SND_SOC_DAPM_PRE_PMD:
...@@ -2269,13 +2377,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ...@@ -2269,13 +2377,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
list_for_each_entry(ctl, &dsp->ctl_list, list) list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0; ctl->enabled = 0;
while (!list_empty(&dsp->alg_regions)) { wm_adsp_free_alg_regions(dsp);
alg_region = list_first_entry(&dsp->alg_regions,
struct wm_adsp_alg_region,
list);
list_del(&alg_region->list);
kfree(alg_region);
}
if (wm_adsp_fw[dsp->fw].num_caps != 0) if (wm_adsp_fw[dsp->fw].num_caps != 0)
wm_adsp_buffer_free(dsp); wm_adsp_buffer_free(dsp);
...@@ -2340,6 +2442,54 @@ int wm_adsp2_init(struct wm_adsp *dsp) ...@@ -2340,6 +2442,54 @@ int wm_adsp2_init(struct wm_adsp *dsp)
} }
EXPORT_SYMBOL_GPL(wm_adsp2_init); EXPORT_SYMBOL_GPL(wm_adsp2_init);
void wm_adsp2_remove(struct wm_adsp *dsp)
{
struct wm_coeff_ctl *ctl;
while (!list_empty(&dsp->ctl_list)) {
ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
list);
list_del(&ctl->list);
wm_adsp_free_ctl_blk(ctl);
}
}
EXPORT_SYMBOL_GPL(wm_adsp2_remove);
static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
{
return compr->buf != NULL;
}
static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
{
/*
* Note this will be more complex once each DSP can support multiple
* streams
*/
if (!compr->dsp->buffer)
return -EINVAL;
compr->buf = compr->dsp->buffer;
compr->buf->compr = compr;
return 0;
}
static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
{
if (!compr)
return;
/* Wake the poll so it can see buffer is no longer attached */
if (compr->stream)
snd_compr_fragment_elapsed(compr->stream);
if (wm_adsp_compr_attached(compr)) {
compr->buf->compr = NULL;
compr->buf = NULL;
}
}
int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
{ {
struct wm_adsp_compr *compr; struct wm_adsp_compr *compr;
...@@ -2393,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream) ...@@ -2393,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
mutex_lock(&dsp->pwr_lock); mutex_lock(&dsp->pwr_lock);
wm_adsp_compr_detach(compr);
dsp->compr = NULL; dsp->compr = NULL;
kfree(compr->raw_buf); kfree(compr->raw_buf);
...@@ -2689,6 +2840,8 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp) ...@@ -2689,6 +2840,8 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
static int wm_adsp_buffer_free(struct wm_adsp *dsp) static int wm_adsp_buffer_free(struct wm_adsp *dsp)
{ {
if (dsp->buffer) { if (dsp->buffer) {
wm_adsp_compr_detach(dsp->buffer->compr);
kfree(dsp->buffer->regions); kfree(dsp->buffer->regions);
kfree(dsp->buffer); kfree(dsp->buffer);
...@@ -2698,25 +2851,6 @@ static int wm_adsp_buffer_free(struct wm_adsp *dsp) ...@@ -2698,25 +2851,6 @@ static int wm_adsp_buffer_free(struct wm_adsp *dsp)
return 0; return 0;
} }
static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
{
return compr->buf != NULL;
}
static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
{
/*
* Note this will be more complex once each DSP can support multiple
* streams
*/
if (!compr->dsp->buffer)
return -EINVAL;
compr->buf = compr->dsp->buffer;
return 0;
}
int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
{ {
struct wm_adsp_compr *compr = stream->runtime->private_data; struct wm_adsp_compr *compr = stream->runtime->private_data;
...@@ -2805,21 +2939,41 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) ...@@ -2805,21 +2939,41 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
avail += wm_adsp_buffer_size(buf); avail += wm_adsp_buffer_size(buf);
adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
buf->read_index, write_index, avail); buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
buf->avail = avail; buf->avail = avail;
return 0; return 0;
} }
static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
{
int ret;
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
if (ret < 0) {
adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
return ret;
}
if (buf->error != 0) {
adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
return -EIO;
}
return 0;
}
int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
{ {
struct wm_adsp_compr_buf *buf = dsp->buffer; struct wm_adsp_compr_buf *buf;
struct wm_adsp_compr *compr = dsp->compr; struct wm_adsp_compr *compr;
int ret = 0; int ret = 0;
mutex_lock(&dsp->pwr_lock); mutex_lock(&dsp->pwr_lock);
buf = dsp->buffer;
compr = dsp->compr;
if (!buf) { if (!buf) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
...@@ -2827,16 +2981,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) ...@@ -2827,16 +2981,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
adsp_dbg(dsp, "Handling buffer IRQ\n"); adsp_dbg(dsp, "Handling buffer IRQ\n");
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); ret = wm_adsp_buffer_get_error(buf);
if (ret < 0) { if (ret < 0)
adsp_err(dsp, "Failed to check buffer error: %d\n", ret); goto out_notify; /* Wake poll to report error */
goto out;
}
if (buf->error != 0) {
adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
ret = -EIO;
goto out;
}
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
&buf->irq_count); &buf->irq_count);
...@@ -2851,6 +2998,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) ...@@ -2851,6 +2998,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
goto out; goto out;
} }
out_notify:
if (compr && compr->stream) if (compr && compr->stream)
snd_compr_fragment_elapsed(compr->stream); snd_compr_fragment_elapsed(compr->stream);
...@@ -2879,14 +3027,16 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, ...@@ -2879,14 +3027,16 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp) struct snd_compr_tstamp *tstamp)
{ {
struct wm_adsp_compr *compr = stream->runtime->private_data; struct wm_adsp_compr *compr = stream->runtime->private_data;
struct wm_adsp_compr_buf *buf = compr->buf;
struct wm_adsp *dsp = compr->dsp; struct wm_adsp *dsp = compr->dsp;
struct wm_adsp_compr_buf *buf;
int ret = 0; int ret = 0;
adsp_dbg(dsp, "Pointer request\n"); adsp_dbg(dsp, "Pointer request\n");
mutex_lock(&dsp->pwr_lock); mutex_lock(&dsp->pwr_lock);
buf = compr->buf;
if (!compr->buf) { if (!compr->buf) {
ret = -ENXIO; ret = -ENXIO;
goto out; goto out;
...@@ -2909,6 +3059,10 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, ...@@ -2909,6 +3059,10 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
* DSP to inform us once a whole fragment is available. * DSP to inform us once a whole fragment is available.
*/ */
if (buf->avail < wm_adsp_compr_frag_words(compr)) { if (buf->avail < wm_adsp_compr_frag_words(compr)) {
ret = wm_adsp_buffer_get_error(buf);
if (ret < 0)
goto out;
ret = wm_adsp_buffer_reenable_irq(buf); ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) { if (ret < 0) {
adsp_err(dsp, adsp_err(dsp,
......
...@@ -92,6 +92,7 @@ extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; ...@@ -92,6 +92,7 @@ extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp1_init(struct wm_adsp *dsp);
int wm_adsp2_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp);
void wm_adsp2_remove(struct wm_adsp *dsp);
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec);
int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
int wm_adsp1_event(struct snd_soc_dapm_widget *w, int wm_adsp1_event(struct snd_soc_dapm_widget *w,
......
...@@ -16,7 +16,11 @@ config SND_EDMA_SOC ...@@ -16,7 +16,11 @@ config SND_EDMA_SOC
- DRA7xx family - DRA7xx family
config SND_DAVINCI_SOC_I2S config SND_DAVINCI_SOC_I2S
tristate tristate "DaVinci Multichannel Buffered Serial Port (McBSP) support"
depends on SND_EDMA_SOC
help
Say Y or M here if you want to have support for McBSP IP found in
Texas Instruments DaVinci DA850 SoCs.
config SND_DAVINCI_SOC_MCASP config SND_DAVINCI_SOC_MCASP
tristate "Multichannel Audio Serial Port (McASP) support" tristate "Multichannel Audio Serial Port (McASP) support"
......
...@@ -4,9 +4,15 @@ ...@@ -4,9 +4,15 @@
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com> * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
* *
* DT support (c) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
* based on davinci-mcasp.c DT support
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*
* TODO:
* on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers
*/ */
#include <linux/init.h> #include <linux/init.h>
...@@ -650,13 +656,24 @@ static const struct snd_soc_component_driver davinci_i2s_component = { ...@@ -650,13 +656,24 @@ static const struct snd_soc_component_driver davinci_i2s_component = {
static int davinci_i2s_probe(struct platform_device *pdev) static int davinci_i2s_probe(struct platform_device *pdev)
{ {
struct snd_dmaengine_dai_dma_data *dma_data;
struct davinci_mcbsp_dev *dev; struct davinci_mcbsp_dev *dev;
struct resource *mem, *res; struct resource *mem, *res;
void __iomem *io_base; void __iomem *io_base;
int *dma; int *dma;
int ret; int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
if (!mem) {
dev_warn(&pdev->dev,
"\"mpu\" mem resource not found, using index 0\n");
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
return -ENODEV;
}
}
io_base = devm_ioremap_resource(&pdev->dev, mem); io_base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(io_base)) if (IS_ERR(io_base))
return PTR_ERR(io_base); return PTR_ERR(io_base);
...@@ -666,39 +683,43 @@ static int davinci_i2s_probe(struct platform_device *pdev) ...@@ -666,39 +683,43 @@ static int davinci_i2s_probe(struct platform_device *pdev)
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk))
return -ENODEV;
clk_enable(dev->clk);
dev->base = io_base; dev->base = io_base;
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = /* setup DMA, first TX, then RX */
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
/* first TX, then RX */
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) { if (res) {
dev_err(&pdev->dev, "no DMA resource\n"); dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
ret = -ENXIO; *dma = res->start;
goto err_release_clk; dma_data->filter_data = dma;
} else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
dma_data->filter_data = "tx";
} else {
dev_err(&pdev->dev, "Missing DMA tx resource\n");
return -ENODEV;
} }
dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
*dma = res->start; dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma; dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
res = platform_get_resource(pdev, IORESOURCE_DMA, 1); res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) { if (res) {
dev_err(&pdev->dev, "no DMA resource\n"); dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
ret = -ENXIO; *dma = res->start;
goto err_release_clk; dma_data->filter_data = dma;
} else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
dma_data->filter_data = "rx";
} else {
dev_err(&pdev->dev, "Missing DMA rx resource\n");
return -ENODEV;
} }
dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
*dma = res->start; dev->clk = clk_get(&pdev->dev, NULL);
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma; if (IS_ERR(dev->clk))
return -ENODEV;
clk_enable(dev->clk);
dev->dev = &pdev->dev; dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dev); dev_set_drvdata(&pdev->dev, dev);
...@@ -737,11 +758,18 @@ static int davinci_i2s_remove(struct platform_device *pdev) ...@@ -737,11 +758,18 @@ static int davinci_i2s_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct of_device_id davinci_i2s_match[] = {
{ .compatible = "ti,da850-mcbsp" },
{},
};
MODULE_DEVICE_TABLE(of, davinci_i2s_match);
static struct platform_driver davinci_mcbsp_driver = { static struct platform_driver davinci_mcbsp_driver = {
.probe = davinci_i2s_probe, .probe = davinci_i2s_probe,
.remove = davinci_i2s_remove, .remove = davinci_i2s_remove,
.driver = { .driver = {
.name = "davinci-mcbsp", .name = "davinci-mcbsp",
.of_match_table = of_match_ptr(davinci_i2s_match),
}, },
}; };
......
...@@ -489,7 +489,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, ...@@ -489,7 +489,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); ACLKX | AFSX | ACLKR | AHCLKR | AFSR);
mcasp->bclk_master = 0; mcasp->bclk_master = 0;
break; break;
default: default:
...@@ -540,21 +540,19 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, ...@@ -540,21 +540,19 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return ret; return ret;
} }
static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
int div, bool explicit) int div, bool explicit)
{ {
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
pm_runtime_get_sync(mcasp->dev); pm_runtime_get_sync(mcasp->dev);
switch (div_id) { switch (div_id) {
case 0: /* MCLK divider */ case MCASP_CLKDIV_AUXCLK: /* MCLK divider */
mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
AHCLKXDIV(div - 1), AHCLKXDIV_MASK); AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRDIV(div - 1), AHCLKRDIV_MASK); AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
break; break;
case 1: /* BCLK divider */ case MCASP_CLKDIV_BCLK: /* BCLK divider */
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
ACLKXDIV(div - 1), ACLKXDIV_MASK); ACLKXDIV(div - 1), ACLKXDIV_MASK);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
...@@ -563,7 +561,8 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, ...@@ -563,7 +561,8 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
mcasp->bclk_div = div; mcasp->bclk_div = div;
break; break;
case 2: /* case MCASP_CLKDIV_BCLK_FS_RATIO:
/*
* BCLK/LRCLK ratio descries how many bit-clock cycles * BCLK/LRCLK ratio descries how many bit-clock cycles
* fit into one frame. The clock ratio is given for a * fit into one frame. The clock ratio is given for a
* full period of data (for I2S format both left and * full period of data (for I2S format both left and
...@@ -591,7 +590,9 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, ...@@ -591,7 +590,9 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
int div) int div)
{ {
return __davinci_mcasp_set_clkdiv(dai, div_id, div, 1); struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1);
} }
static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
...@@ -999,27 +1000,53 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, ...@@ -999,27 +1000,53 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
} }
static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
unsigned int bclk_freq, unsigned int bclk_freq, bool set)
int *error_ppm)
{ {
int div = mcasp->sysclk_freq / bclk_freq; int error_ppm;
int rem = mcasp->sysclk_freq % bclk_freq; unsigned int sysclk_freq = mcasp->sysclk_freq;
u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
int div = sysclk_freq / bclk_freq;
int rem = sysclk_freq % bclk_freq;
int aux_div = 1;
if (div > (ACLKXDIV_MASK + 1)) {
if (reg & AHCLKXE) {
aux_div = div / (ACLKXDIV_MASK + 1);
if (div % (ACLKXDIV_MASK + 1))
aux_div++;
sysclk_freq /= aux_div;
div = sysclk_freq / bclk_freq;
rem = sysclk_freq % bclk_freq;
} else if (set) {
dev_warn(mcasp->dev, "Too fast reference clock (%u)\n",
sysclk_freq);
}
}
if (rem != 0) { if (rem != 0) {
if (div == 0 || if (div == 0 ||
((mcasp->sysclk_freq / div) - bclk_freq) > ((sysclk_freq / div) - bclk_freq) >
(bclk_freq - (mcasp->sysclk_freq / (div+1)))) { (bclk_freq - (sysclk_freq / (div+1)))) {
div++; div++;
rem = rem - bclk_freq; rem = rem - bclk_freq;
} }
} }
if (error_ppm) error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem,
*error_ppm = (int)bclk_freq)) / div - 1000000;
(div*1000000 + (int)div64_long(1000000LL*rem,
(int)bclk_freq)) if (set) {
/div - 1000000; if (error_ppm)
dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
error_ppm);
__davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
if (reg & AHCLKXE)
__davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
aux_div, 0);
}
return div; return error_ppm;
} }
static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
...@@ -1044,18 +1071,11 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, ...@@ -1044,18 +1071,11 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int slots = mcasp->tdm_slots; int slots = mcasp->tdm_slots;
int rate = params_rate(params); int rate = params_rate(params);
int sbits = params_width(params); int sbits = params_width(params);
int ppm, div;
if (mcasp->slot_width) if (mcasp->slot_width)
sbits = mcasp->slot_width; sbits = mcasp->slot_width;
div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true);
&ppm);
if (ppm)
dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
ppm);
__davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0);
} }
ret = mcasp_common_hw_param(mcasp, substream->stream, ret = mcasp_common_hw_param(mcasp, substream->stream,
...@@ -1166,7 +1186,8 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, ...@@ -1166,7 +1186,8 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
davinci_mcasp_dai_rates[i]; davinci_mcasp_dai_rates[i];
int ppm; int ppm;
davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq,
false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
if (range.empty) { if (range.empty) {
range.min = davinci_mcasp_dai_rates[i]; range.min = davinci_mcasp_dai_rates[i];
...@@ -1205,8 +1226,9 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, ...@@ -1205,8 +1226,9 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
if (rd->mcasp->slot_width) if (rd->mcasp->slot_width)
sbits = rd->mcasp->slot_width; sbits = rd->mcasp->slot_width;
davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate, ppm = davinci_mcasp_calc_clk_div(rd->mcasp,
&ppm); sbits * slots * rate,
false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set(&nfmt, i); snd_mask_set(&nfmt, i);
count++; count++;
...@@ -1230,11 +1252,15 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, ...@@ -1230,11 +1252,15 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
int i, dir; int i, dir;
int tdm_slots = mcasp->tdm_slots; int tdm_slots = mcasp->tdm_slots;
if (mcasp->tdm_mask[substream->stream]) /* Do not allow more then one stream per direction */
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); if (mcasp->substreams[substream->stream])
return -EBUSY;
mcasp->substreams[substream->stream] = substream; mcasp->substreams[substream->stream] = substream;
if (mcasp->tdm_mask[substream->stream])
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
return 0; return 0;
......
...@@ -306,4 +306,9 @@ ...@@ -306,4 +306,9 @@
#define NUMEVT(x) (((x) & 0xFF) << 8) #define NUMEVT(x) (((x) & 0xFF) << 8)
#define NUMDMA_MASK (0xFF) #define NUMDMA_MASK (0xFF)
/* clock divider IDs */
#define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */
#define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */
#define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */
#endif /* DAVINCI_MCASP_H */ #endif /* DAVINCI_MCASP_H */
...@@ -100,6 +100,7 @@ struct dw_i2s_dev { ...@@ -100,6 +100,7 @@ struct dw_i2s_dev {
struct device *dev; struct device *dev;
u32 ccr; u32 ccr;
u32 xfer_resolution; u32 xfer_resolution;
u32 fifo_th;
/* data related to DMA transfers b/w i2s and DMAC */ /* data related to DMA transfers b/w i2s and DMAC */
union dw_i2s_snd_dma_data play_dma_data; union dw_i2s_snd_dma_data play_dma_data;
...@@ -147,17 +148,18 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) ...@@ -147,17 +148,18 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
static void i2s_start(struct dw_i2s_dev *dev, static void i2s_start(struct dw_i2s_dev *dev,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct i2s_clk_config_data *config = &dev->config;
u32 i, irq; u32 i, irq;
i2s_write_reg(dev->i2s_base, IER, 1); i2s_write_reg(dev->i2s_base, IER, 1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < 4; i++) { for (i = 0; i < (config->chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i)); irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30); i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
} }
i2s_write_reg(dev->i2s_base, ITER, 1); i2s_write_reg(dev->i2s_base, ITER, 1);
} else { } else {
for (i = 0; i < 4; i++) { for (i = 0; i < (config->chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i)); irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03); i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
} }
...@@ -231,14 +233,16 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) ...@@ -231,14 +233,16 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
i2s_write_reg(dev->i2s_base, TCR(ch_reg), i2s_write_reg(dev->i2s_base, TCR(ch_reg),
dev->xfer_resolution); dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
dev->fifo_th - 1);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
} else { } else {
i2s_write_reg(dev->i2s_base, RCR(ch_reg), i2s_write_reg(dev->i2s_base, RCR(ch_reg),
dev->xfer_resolution); dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
dev->fifo_th - 1);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
...@@ -498,6 +502,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, ...@@ -498,6 +502,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
*/ */
u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2); u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
u32 idx; u32 idx;
if (dev->capability & DWC_I2S_RECORD && if (dev->capability & DWC_I2S_RECORD &&
...@@ -536,6 +541,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, ...@@ -536,6 +541,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
dev->capability |= DW_I2S_SLAVE; dev->capability |= DW_I2S_SLAVE;
} }
dev->fifo_th = fifo_depth / 2;
return 0; return 0;
} }
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include "fsl_sai.h" #include "fsl_sai.h"
#include "imx-pcm.h" #include "imx-pcm.h"
...@@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct fsl_sai *sai; struct fsl_sai *sai;
struct regmap *gpr;
struct resource *res; struct resource *res;
void __iomem *base; void __iomem *base;
char tmp[8]; char tmp[8];
int irq, ret, i; int irq, ret, i;
int index;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai) if (!sai)
...@@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->pdev = pdev; sai->pdev = pdev;
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") ||
of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai"))
sai->sai_on_imx = true; sai->sai_on_imx = true;
sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
...@@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev)
fsl_sai_dai.symmetric_samplebits = 0; fsl_sai_dai.symmetric_samplebits = 0;
} }
if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) {
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
if (IS_ERR(gpr)) {
dev_err(&pdev->dev, "cannot find iomuxc registers\n");
return PTR_ERR(gpr);
}
index = of_alias_get_id(np, "sai");
if (index < 0)
return index;
regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index),
MCLK_DIR(index));
}
sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
...@@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev) ...@@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
static const struct of_device_id fsl_sai_ids[] = { static const struct of_device_id fsl_sai_ids[] = {
{ .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,vf610-sai", },
{ .compatible = "fsl,imx6sx-sai", }, { .compatible = "fsl,imx6sx-sai", },
{ .compatible = "fsl,imx6ul-sai", },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, fsl_sai_ids); MODULE_DEVICE_TABLE(of, fsl_sai_ids);
......
...@@ -137,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) ...@@ -137,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
case CCSR_SSI_SACDAT: case CCSR_SSI_SACDAT:
case CCSR_SSI_SATAG: case CCSR_SSI_SATAG:
case CCSR_SSI_SACCST: case CCSR_SSI_SACCST:
case CCSR_SSI_SOR:
return true; return true;
default: default:
return false; return false;
...@@ -261,6 +262,7 @@ struct fsl_ssi_private { ...@@ -261,6 +262,7 @@ struct fsl_ssi_private {
struct fsl_ssi_dbg dbg_stats; struct fsl_ssi_dbg dbg_stats;
const struct fsl_ssi_soc_data *soc; const struct fsl_ssi_soc_data *soc;
struct device *dev;
}; };
/* /*
...@@ -399,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, ...@@ -399,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
} }
} }
/*
* Clear RX or TX FIFO to remove samples from the previous
* stream session which may be still present in the FIFO and
* may introduce bad samples and/or channel slipping.
*
* Note: The SOR is not documented in recent IMX datasheet, but
* is described in IMX51 reference manual at section 56.3.3.15.
*/
static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private,
bool is_rx)
{
if (is_rx) {
regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR);
} else {
regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR);
}
}
/* /*
* Calculate the bits that have to be disabled for the current stream that is * Calculate the bits that have to be disabled for the current stream that is
* getting disabled. This keeps the bits enabled that are necessary for the * getting disabled. This keeps the bits enabled that are necessary for the
...@@ -474,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -474,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
* (online configuration) * (online configuration)
*/ */
if (enable) { if (enable) {
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE);
regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
} else { } else {
u32 sier; u32 sier;
u32 srcr; u32 srcr;
...@@ -506,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, ...@@ -506,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
config_done: config_done:
/* Enabling of subunits is done after configuration */ /* Enabling of subunits is done after configuration */
if (enable) if (enable) {
if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) {
/*
* Be sure the Tx FIFO is filled when TE is set.
* Otherwise, there are some chances to start the
* playback with some void samples inserted first,
* generating a channel slip.
*
* First, SSIEN must be set, to let the FIFO be filled.
*
* Notes:
* - Limit this fix to the DMA case until FIQ cases can
* be tested.
* - Limit the length of the busy loop to not lock the
* system too long, even if 1-2 loops are sufficient
* in general.
*/
int i;
int max_loop = 100;
regmap_update_bits(regs, CCSR_SSI_SCR,
CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);
for (i = 0; i < max_loop; i++) {
u32 sfcsr;
regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr);
if (CCSR_SSI_SFCSR_TFCNT0(sfcsr))
break;
}
if (i == max_loop) {
dev_err(ssi_private->dev,
"Timeout waiting TX FIFO filling\n");
}
}
regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr); regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
}
} }
...@@ -670,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -670,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
if (IS_ERR(ssi_private->baudclk)) if (IS_ERR(ssi_private->baudclk))
return -EINVAL; return -EINVAL;
/*
* Hardware limitation: The bclk rate must be
* never greater than 1/5 IPG clock rate
*/
if (freq * 5 > clk_get_rate(ssi_private->clk)) {
dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n");
return -EINVAL;
}
baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream)); baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
/* It should be already enough to divide clock by setting pm alone */ /* It should be already enough to divide clock by setting pm alone */
...@@ -686,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, ...@@ -686,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
else else
clkrate = clk_round_rate(ssi_private->baudclk, tmprate); clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
/*
* Hardware limitation: The bclk rate must be
* never greater than 1/5 IPG clock rate
*/
if (clkrate * 5 > clk_get_rate(ssi_private->clk))
continue;
clkrate /= factor; clkrate /= factor;
afreq = clkrate / (i + 1); afreq = clkrate / (i + 1);
...@@ -1158,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = { ...@@ -1158,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.playback = { .playback = {
.stream_name = "CPU-Playback", .stream_name = "CPU-Playback",
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 32,
.rates = FSLSSI_I2S_RATES, .rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS, .formats = FSLSSI_I2S_FORMATS,
}, },
.capture = { .capture = {
.stream_name = "CPU-Capture", .stream_name = "CPU-Capture",
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 32,
.rates = FSLSSI_I2S_RATES, .rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS, .formats = FSLSSI_I2S_FORMATS,
}, },
...@@ -1402,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ...@@ -1402,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
} }
ssi_private->soc = of_id->data; ssi_private->soc = of_id->data;
ssi_private->dev = &pdev->dev;
sprop = of_get_property(np, "fsl,mode", NULL); sprop = of_get_property(np, "fsl,mode", NULL);
if (sprop) { if (sprop) {
......
...@@ -220,7 +220,7 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, ...@@ -220,7 +220,7 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
ret = dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, ret = dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
runtime->dma_addr, runtime->dma_bytes); runtime->dma_addr, runtime->dma_bytes);
pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret, pr_debug("%s: ret: %d %p %pad 0x%08zx\n", __func__, ret,
runtime->dma_area, runtime->dma_area,
&runtime->dma_addr, &runtime->dma_addr,
runtime->dma_bytes); runtime->dma_bytes);
......
...@@ -58,6 +58,21 @@ config SND_SOC_INTEL_HASWELL_MACH ...@@ -58,6 +58,21 @@ config SND_SOC_INTEL_HASWELL_MACH
Say Y if you have such a device Say Y if you have such a device
If unsure select "N". If unsure select "N".
config SND_SOC_INTEL_BXT_RT298_MACH
tristate "ASoC Audio driver for Broxton with RT298 I2S mode"
depends on X86 && ACPI && I2C
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SKYLAKE
select SND_SOC_RT298
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
select SND_HDA_DSP_LOADER
help
This adds support for ASoC machine driver for Broxton platforms
with RT286 I2S audio codec.
Say Y if you have such a device
If unsure select "N".
config SND_SOC_INTEL_BYT_RT5640_MACH config SND_SOC_INTEL_BYT_RT5640_MACH
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
depends on X86_INTEL_LPSS && I2C depends on X86_INTEL_LPSS && I2C
...@@ -162,6 +177,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH ...@@ -162,6 +177,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
config SND_SOC_INTEL_SKYLAKE config SND_SOC_INTEL_SKYLAKE
tristate tristate
select SND_HDA_EXT_CORE select SND_HDA_EXT_CORE
select SND_HDA_DSP_LOADER
select SND_SOC_TOPOLOGY select SND_SOC_TOPOLOGY
select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST
......
...@@ -195,7 +195,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol ...@@ -195,7 +195,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol
if (e->w && e->w->power) if (e->w && e->w->power)
ret = sst_send_slot_map(drv); ret = sst_send_slot_map(drv);
else else if (!e->w)
dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n", dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
kcontrol->id.name); kcontrol->id.name);
return ret; return ret;
......
...@@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o ...@@ -2,6 +2,7 @@ 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-broadwell-objs := broadwell.o snd-soc-sst-broadwell-objs := broadwell.o
snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
...@@ -14,6 +15,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o ...@@ -14,6 +15,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.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_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
......
...@@ -201,7 +201,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { ...@@ -201,7 +201,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
{ {
/* SSP0 - Codec */ /* SSP0 - Codec */
.name = "Codec", .name = "Codec",
.be_id = 0, .id = 0,
.cpu_dai_name = "snd-soc-dummy-dai", .cpu_dai_name = "snd-soc-dummy-dai",
.platform_name = "snd-soc-dummy", .platform_name = "snd-soc-dummy",
.no_pcm = 1, .no_pcm = 1,
......
/*
* Intel Broxton-P I2S Machine Driver
*
* Copyright (C) 2014-2016, Intel Corporation. All rights reserved.
*
* Modified from:
* Intel Skylake I2S Machine driver
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License 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.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include "../../codecs/hdac_hdmi.h"
#include "../../codecs/rt298.h"
static struct snd_soc_jack broxton_headset;
/* Headset jack detection DAPM pins */
enum {
BXT_DPCM_AUDIO_PB = 0,
BXT_DPCM_AUDIO_CP,
BXT_DPCM_AUDIO_REF_CP,
BXT_DPCM_AUDIO_HDMI1_PB,
BXT_DPCM_AUDIO_HDMI2_PB,
BXT_DPCM_AUDIO_HDMI3_PB,
};
static struct snd_soc_jack_pin broxton_headset_pins[] = {
{
.pin = "Mic Jack",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
};
static const struct snd_kcontrol_new broxton_controls[] = {
SOC_DAPM_PIN_SWITCH("Speaker"),
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Mic Jack"),
};
static const struct snd_soc_dapm_widget broxton_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("DMIC2", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
SND_SOC_DAPM_SPK("HDMI1", NULL),
SND_SOC_DAPM_SPK("HDMI2", NULL),
SND_SOC_DAPM_SPK("HDMI3", NULL),
};
static const struct snd_soc_dapm_route broxton_rt298_map[] = {
/* speaker */
{"Speaker", NULL, "SPOR"},
{"Speaker", NULL, "SPOL"},
/* HP jack connectors - unknown if we have jack detect */
{"Headphone Jack", NULL, "HPO Pin"},
/* other jacks */
{"MIC1", NULL, "Mic Jack"},
/* digital mics */
{"DMIC1 Pin", NULL, "DMIC2"},
{"DMic", NULL, "SoC DMIC"},
{"HDMI1", NULL, "hif5 Output"},
{"HDMI2", NULL, "hif6 Output"},
{"HDMI3", NULL, "hif7 Output"},
/* CODEC BE connections */
{ "AIF1 Playback", NULL, "ssp5 Tx"},
{ "ssp5 Tx", NULL, "codec0_out"},
{ "codec0_in", NULL, "ssp5 Rx" },
{ "ssp5 Rx", NULL, "AIF1 Capture" },
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "Capture" },
{ "hifi3", NULL, "iDisp3 Tx"},
{ "iDisp3 Tx", NULL, "iDisp3_out"},
{ "hifi2", NULL, "iDisp2 Tx"},
{ "iDisp2 Tx", NULL, "iDisp2_out"},
{ "hifi1", NULL, "iDisp1 Tx"},
{ "iDisp1 Tx", NULL, "iDisp1_out"},
};
static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
int ret = 0;
ret = snd_soc_card_jack_new(rtd->card, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&broxton_headset,
broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins));
if (ret)
return ret;
rt298_mic_detect(codec, &broxton_headset);
return 0;
}
static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
}
static int broxton_ssp5_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);
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
/* The ADSP will covert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP5 to 24 bit */
snd_mask_none(fmt);
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
return 0;
}
static int broxton_rt298_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, RT298_SCLK_S_PLL,
19200000, 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 broxton_rt298_ops = {
.hw_params = broxton_rt298_hw_params,
};
/* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_rt298_dais[] = {
/* Front End DAI links */
[BXT_DPCM_AUDIO_PB]
{
.name = "Bxt Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
.platform_name = "0000:00:0e.0",
.nonatomic = 1,
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
},
[BXT_DPCM_AUDIO_CP]
{
.name = "Bxt Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
.platform_name = "0000:00:0e.0",
.nonatomic = 1,
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
},
[BXT_DPCM_AUDIO_REF_CP]
{
.name = "Bxt Audio Reference cap",
.stream_name = "refcap",
.cpu_dai_name = "Reference 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,
},
[BXT_DPCM_AUDIO_HDMI1_PB]
{
.name = "Bxt HDMI Port1",
.stream_name = "Hdmi1",
.cpu_dai_name = "HDMI1 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
[BXT_DPCM_AUDIO_HDMI2_PB]
{
.name = "Bxt HDMI Port2",
.stream_name = "Hdmi2",
.cpu_dai_name = "HDMI2 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
[BXT_DPCM_AUDIO_HDMI3_PB]
{
.name = "Bxt HDMI Port3",
.stream_name = "Hdmi3",
.cpu_dai_name = "HDMI3 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:0e.0",
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
/* Back End DAI links */
{
/* SSP5 - Codec */
.name = "SSP5-Codec",
.id = 0,
.cpu_dai_name = "SSP5 Pin",
.platform_name = "0000:00:0e.0",
.no_pcm = 1,
.codec_name = "i2c-INT343A:00",
.codec_dai_name = "rt298-aif1",
.init = broxton_rt298_codec_init,
.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broxton_ssp5_fixup,
.ops = &broxton_rt298_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{
.name = "dmic01",
.id = 1,
.cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
.platform_name = "0000:00:0e.0",
.ignore_suspend = 1,
.dpcm_capture = 1,
.no_pcm = 1,
},
{
.name = "iDisp1",
.id = 3,
.cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:0e.0",
.init = broxton_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp2",
.id = 4,
.cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2",
.platform_name = "0000:00:0e.0",
.init = broxton_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
{
.name = "iDisp3",
.id = 5,
.cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3",
.platform_name = "0000:00:0e.0",
.init = broxton_hdmi_init,
.dpcm_playback = 1,
.no_pcm = 1,
},
};
/* broxton audio machine driver for SPT + RT298S */
static struct snd_soc_card broxton_rt298 = {
.name = "broxton-rt298",
.owner = THIS_MODULE,
.dai_link = broxton_rt298_dais,
.num_links = ARRAY_SIZE(broxton_rt298_dais),
.controls = broxton_controls,
.num_controls = ARRAY_SIZE(broxton_controls),
.dapm_widgets = broxton_widgets,
.num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
.dapm_routes = broxton_rt298_map,
.num_dapm_routes = ARRAY_SIZE(broxton_rt298_map),
.fully_routed = true,
};
static int broxton_audio_probe(struct platform_device *pdev)
{
broxton_rt298.dev = &pdev->dev;
return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298);
}
static struct platform_driver broxton_audio = {
.probe = broxton_audio_probe,
.driver = {
.name = "bxt_alc298s_i2s",
},
};
module_platform_driver(broxton_audio)
/* Module information */
MODULE_AUTHOR("Ramesh Babu <Ramesh.Babu@intel.com>");
MODULE_AUTHOR("Senthilnathan Veppur <senthilnathanx.veppur@intel.com>");
MODULE_DESCRIPTION("Intel SST Audio for Broxton");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bxt_alc298s_i2s");
...@@ -304,7 +304,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { ...@@ -304,7 +304,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
/* back ends */ /* back ends */
{ {
.name = "SSP2-Codec", .name = "SSP2-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "ssp2-port", .cpu_dai_name = "ssp2-port",
.platform_name = "sst-mfld-platform", .platform_name = "sst-mfld-platform",
.no_pcm = 1, .no_pcm = 1,
......
...@@ -267,7 +267,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { ...@@ -267,7 +267,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
/* back ends */ /* back ends */
{ {
.name = "SSP2-Codec", .name = "SSP2-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "ssp2-port", .cpu_dai_name = "ssp2-port",
.platform_name = "sst-mfld-platform", .platform_name = "sst-mfld-platform",
.no_pcm = 1, .no_pcm = 1,
......
...@@ -255,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = { ...@@ -255,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
/* back ends */ /* back ends */
{ {
.name = "SSP2-Codec", .name = "SSP2-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "ssp2-port", .cpu_dai_name = "ssp2-port",
.platform_name = "sst-mfld-platform", .platform_name = "sst-mfld-platform",
.no_pcm = 1, .no_pcm = 1,
......
...@@ -295,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = { ...@@ -295,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
/* back ends */ /* back ends */
{ {
.name = "SSP2-Codec", .name = "SSP2-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "ssp2-port", .cpu_dai_name = "ssp2-port",
.platform_name = "sst-mfld-platform", .platform_name = "sst-mfld-platform",
.no_pcm = 1, .no_pcm = 1,
......
...@@ -273,7 +273,7 @@ static struct snd_soc_dai_link cht_dailink[] = { ...@@ -273,7 +273,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
{ {
/* SSP2 - Codec */ /* SSP2 - Codec */
.name = "SSP2-Codec", .name = "SSP2-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "ssp2-port", .cpu_dai_name = "ssp2-port",
.platform_name = "sst-mfld-platform", .platform_name = "sst-mfld-platform",
.no_pcm = 1, .no_pcm = 1,
......
...@@ -156,7 +156,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { ...@@ -156,7 +156,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
{ {
/* SSP0 - Codec */ /* SSP0 - Codec */
.name = "Codec", .name = "Codec",
.be_id = 0, .id = 0,
.cpu_dai_name = "snd-soc-dummy-dai", .cpu_dai_name = "snd-soc-dummy-dai",
.platform_name = "snd-soc-dummy", .platform_name = "snd-soc-dummy",
.no_pcm = 1, .no_pcm = 1,
......
...@@ -30,6 +30,16 @@ ...@@ -30,6 +30,16 @@
static struct snd_soc_jack skylake_headset; static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card; static struct snd_soc_card skylake_audio_card;
struct skl_hdmi_pcm {
struct list_head head;
struct snd_soc_dai *codec_dai;
int device;
};
struct skl_nau8825_private {
struct list_head hdmi_pcm_list;
};
enum { enum {
SKL_DPCM_AUDIO_PB = 0, SKL_DPCM_AUDIO_PB = 0,
SKL_DPCM_AUDIO_CP, SKL_DPCM_AUDIO_CP,
...@@ -192,23 +202,56 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) ...@@ -192,23 +202,56 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
pcm->codec_dai = dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return 0;
} }
static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
pcm->codec_dai = dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return 0;
} }
static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
pcm->codec_dai = dai;
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return 0;
} }
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
...@@ -391,7 +434,6 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -391,7 +434,6 @@ static struct snd_soc_dai_link skylake_dais[] = {
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.init = NULL, .init = NULL,
.dpcm_capture = 1, .dpcm_capture = 1,
.ignore_suspend = 1,
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
.ops = &skylaye_refcap_ops, .ops = &skylaye_refcap_ops,
...@@ -456,7 +498,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -456,7 +498,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
{ {
/* SSP0 - Codec */ /* SSP0 - Codec */
.name = "SSP0-Codec", .name = "SSP0-Codec",
.be_id = 0, .id = 0,
.cpu_dai_name = "SSP0 Pin", .cpu_dai_name = "SSP0 Pin",
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.no_pcm = 1, .no_pcm = 1,
...@@ -472,7 +514,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -472,7 +514,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
{ {
/* SSP1 - Codec */ /* SSP1 - Codec */
.name = "SSP1-Codec", .name = "SSP1-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "SSP1 Pin", .cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.no_pcm = 1, .no_pcm = 1,
...@@ -489,7 +531,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -489,7 +531,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "dmic01", .name = "dmic01",
.be_id = 2, .id = 2,
.cpu_dai_name = "DMIC01 Pin", .cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec", .codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi", .codec_dai_name = "dmic-hifi",
...@@ -501,7 +543,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -501,7 +543,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "iDisp1", .name = "iDisp1",
.be_id = 3, .id = 3,
.cpu_dai_name = "iDisp1 Pin", .cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1", .codec_dai_name = "intel-hdmi-hifi1",
...@@ -512,7 +554,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -512,7 +554,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "iDisp2", .name = "iDisp2",
.be_id = 4, .id = 4,
.cpu_dai_name = "iDisp2 Pin", .cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2", .codec_dai_name = "intel-hdmi-hifi2",
...@@ -523,7 +565,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -523,7 +565,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "iDisp3", .name = "iDisp3",
.be_id = 5, .id = 5,
.cpu_dai_name = "iDisp3 Pin", .cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3", .codec_dai_name = "intel-hdmi-hifi3",
...@@ -534,6 +576,21 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -534,6 +576,21 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
}; };
static int skylake_card_late_probe(struct snd_soc_card *card)
{
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(card);
struct skl_hdmi_pcm *pcm;
int err;
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
if (err < 0)
return err;
}
return 0;
}
/* skylake audio machine driver for SPT + NAU88L25 */ /* skylake audio machine driver for SPT + NAU88L25 */
static struct snd_soc_card skylake_audio_card = { static struct snd_soc_card skylake_audio_card = {
.name = "sklnau8825max", .name = "sklnau8825max",
...@@ -547,11 +604,21 @@ static struct snd_soc_card skylake_audio_card = { ...@@ -547,11 +604,21 @@ static struct snd_soc_card skylake_audio_card = {
.dapm_routes = skylake_map, .dapm_routes = skylake_map,
.num_dapm_routes = ARRAY_SIZE(skylake_map), .num_dapm_routes = ARRAY_SIZE(skylake_map),
.fully_routed = true, .fully_routed = true,
.late_probe = skylake_card_late_probe,
}; };
static int skylake_audio_probe(struct platform_device *pdev) static int skylake_audio_probe(struct platform_device *pdev)
{ {
struct skl_nau8825_private *ctx;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return -ENOMEM;
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
skylake_audio_card.dev = &pdev->dev; skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
} }
......
...@@ -34,6 +34,15 @@ ...@@ -34,6 +34,15 @@
static struct snd_soc_jack skylake_headset; static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card; static struct snd_soc_card skylake_audio_card;
struct skl_hdmi_pcm {
struct list_head head;
struct snd_soc_dai *codec_dai;
int device;
};
struct skl_nau88125_private {
struct list_head hdmi_pcm_list;
};
enum { enum {
SKL_DPCM_AUDIO_PB = 0, SKL_DPCM_AUDIO_PB = 0,
SKL_DPCM_AUDIO_CP, SKL_DPCM_AUDIO_CP,
...@@ -222,24 +231,57 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) ...@@ -222,24 +231,57 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
pcm->codec_dai = dai;
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); return 0;
} }
static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
pcm->codec_dai = dai;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return 0;
} }
static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
pcm->codec_dai = dai;
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return 0;
} }
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
...@@ -440,7 +482,6 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -440,7 +482,6 @@ static struct snd_soc_dai_link skylake_dais[] = {
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.init = NULL, .init = NULL,
.dpcm_capture = 1, .dpcm_capture = 1,
.ignore_suspend = 1,
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
.ops = &skylaye_refcap_ops, .ops = &skylaye_refcap_ops,
...@@ -505,7 +546,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -505,7 +546,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
{ {
/* SSP0 - Codec */ /* SSP0 - Codec */
.name = "SSP0-Codec", .name = "SSP0-Codec",
.be_id = 0, .id = 0,
.cpu_dai_name = "SSP0 Pin", .cpu_dai_name = "SSP0 Pin",
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.no_pcm = 1, .no_pcm = 1,
...@@ -523,7 +564,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -523,7 +564,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
{ {
/* SSP1 - Codec */ /* SSP1 - Codec */
.name = "SSP1-Codec", .name = "SSP1-Codec",
.be_id = 1, .id = 1,
.cpu_dai_name = "SSP1 Pin", .cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.no_pcm = 1, .no_pcm = 1,
...@@ -540,7 +581,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -540,7 +581,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "dmic01", .name = "dmic01",
.be_id = 2, .id = 2,
.cpu_dai_name = "DMIC01 Pin", .cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec", .codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi", .codec_dai_name = "dmic-hifi",
...@@ -552,7 +593,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -552,7 +593,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "iDisp1", .name = "iDisp1",
.be_id = 3, .id = 3,
.cpu_dai_name = "iDisp1 Pin", .cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1", .codec_dai_name = "intel-hdmi-hifi1",
...@@ -563,7 +604,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -563,7 +604,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "iDisp2", .name = "iDisp2",
.be_id = 4, .id = 4,
.cpu_dai_name = "iDisp2 Pin", .cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2", .codec_dai_name = "intel-hdmi-hifi2",
...@@ -574,7 +615,7 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -574,7 +615,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
{ {
.name = "iDisp3", .name = "iDisp3",
.be_id = 5, .id = 5,
.cpu_dai_name = "iDisp3 Pin", .cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3", .codec_dai_name = "intel-hdmi-hifi3",
...@@ -585,6 +626,21 @@ static struct snd_soc_dai_link skylake_dais[] = { ...@@ -585,6 +626,21 @@ static struct snd_soc_dai_link skylake_dais[] = {
}, },
}; };
static int skylake_card_late_probe(struct snd_soc_card *card)
{
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(card);
struct skl_hdmi_pcm *pcm;
int err;
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
if (err < 0)
return err;
}
return 0;
}
/* skylake audio machine driver for SPT + NAU88L25 */ /* skylake audio machine driver for SPT + NAU88L25 */
static struct snd_soc_card skylake_audio_card = { static struct snd_soc_card skylake_audio_card = {
.name = "sklnau8825adi", .name = "sklnau8825adi",
...@@ -600,11 +656,21 @@ static struct snd_soc_card skylake_audio_card = { ...@@ -600,11 +656,21 @@ static struct snd_soc_card skylake_audio_card = {
.codec_conf = ssm4567_codec_conf, .codec_conf = ssm4567_codec_conf,
.num_configs = ARRAY_SIZE(ssm4567_codec_conf), .num_configs = ARRAY_SIZE(ssm4567_codec_conf),
.fully_routed = true, .fully_routed = true,
.late_probe = skylake_card_late_probe,
}; };
static int skylake_audio_probe(struct platform_device *pdev) static int skylake_audio_probe(struct platform_device *pdev)
{ {
struct skl_nau88125_private *ctx;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return -ENOMEM;
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
skylake_audio_card.dev = &pdev->dev; skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
} }
......
...@@ -30,6 +30,16 @@ ...@@ -30,6 +30,16 @@
static struct snd_soc_jack skylake_headset; static struct snd_soc_jack skylake_headset;
struct skl_hdmi_pcm {
struct list_head head;
struct snd_soc_dai *codec_dai;
int device;
};
struct skl_rt286_private {
struct list_head hdmi_pcm_list;
};
enum { enum {
SKL_DPCM_AUDIO_PB = 0, SKL_DPCM_AUDIO_PB = 0,
SKL_DPCM_AUDIO_CP, SKL_DPCM_AUDIO_CP,
...@@ -142,9 +152,20 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) ...@@ -142,9 +152,20 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai = rtd->codec_dai; struct snd_soc_dai *dai = rtd->codec_dai;
struct skl_hdmi_pcm *pcm;
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id); pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id;
pcm->codec_dai = dai;
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
return 0;
} }
static unsigned int rates[] = { static unsigned int rates[] = {
...@@ -317,7 +338,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -317,7 +338,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.init = NULL, .init = NULL,
.dpcm_capture = 1, .dpcm_capture = 1,
.ignore_suspend = 1,
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
}, },
...@@ -375,7 +395,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -375,7 +395,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
{ {
/* SSP0 - Codec */ /* SSP0 - Codec */
.name = "SSP0-Codec", .name = "SSP0-Codec",
.be_id = 0, .id = 0,
.cpu_dai_name = "SSP0 Pin", .cpu_dai_name = "SSP0 Pin",
.platform_name = "0000:00:1f.3", .platform_name = "0000:00:1f.3",
.no_pcm = 1, .no_pcm = 1,
...@@ -393,7 +413,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -393,7 +413,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
}, },
{ {
.name = "dmic01", .name = "dmic01",
.be_id = 1, .id = 1,
.cpu_dai_name = "DMIC01 Pin", .cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec", .codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi", .codec_dai_name = "dmic-hifi",
...@@ -405,7 +425,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -405,7 +425,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
}, },
{ {
.name = "iDisp1", .name = "iDisp1",
.be_id = 2, .id = 2,
.cpu_dai_name = "iDisp1 Pin", .cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1", .codec_dai_name = "intel-hdmi-hifi1",
...@@ -416,7 +436,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -416,7 +436,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
}, },
{ {
.name = "iDisp2", .name = "iDisp2",
.be_id = 3, .id = 3,
.cpu_dai_name = "iDisp2 Pin", .cpu_dai_name = "iDisp2 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi2", .codec_dai_name = "intel-hdmi-hifi2",
...@@ -427,7 +447,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -427,7 +447,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
}, },
{ {
.name = "iDisp3", .name = "iDisp3",
.be_id = 4, .id = 4,
.cpu_dai_name = "iDisp3 Pin", .cpu_dai_name = "iDisp3 Pin",
.codec_name = "ehdaudio0D2", .codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi3", .codec_dai_name = "intel-hdmi-hifi3",
...@@ -438,6 +458,21 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { ...@@ -438,6 +458,21 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
}, },
}; };
static int skylake_card_late_probe(struct snd_soc_card *card)
{
struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card);
struct skl_hdmi_pcm *pcm;
int err;
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
if (err < 0)
return err;
}
return 0;
}
/* skylake audio machine driver for SPT + RT286S */ /* skylake audio machine driver for SPT + RT286S */
static struct snd_soc_card skylake_rt286 = { static struct snd_soc_card skylake_rt286 = {
.name = "skylake-rt286", .name = "skylake-rt286",
...@@ -451,11 +486,21 @@ static struct snd_soc_card skylake_rt286 = { ...@@ -451,11 +486,21 @@ static struct snd_soc_card skylake_rt286 = {
.dapm_routes = skylake_rt286_map, .dapm_routes = skylake_rt286_map,
.num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
.fully_routed = true, .fully_routed = true,
.late_probe = skylake_card_late_probe,
}; };
static int skylake_audio_probe(struct platform_device *pdev) static int skylake_audio_probe(struct platform_device *pdev)
{ {
struct skl_rt286_private *ctx;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return -ENOMEM;
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
skylake_rt286.dev = &pdev->dev; skylake_rt286.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_rt286, ctx);
return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
} }
......
...@@ -12,10 +12,19 @@ ...@@ -12,10 +12,19 @@
* *
*/ */
#include <linux/kconfig.h>
#include <linux/stddef.h>
#include <linux/acpi.h> #include <linux/acpi.h>
/* translation fron HID to I2C name, needed for DAI codec_name */ /* translation fron HID to I2C name, needed for DAI codec_name */
#if IS_ENABLED(CONFIG_ACPI)
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
#else
inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
{
return NULL;
}
#endif
/* acpi match */ /* acpi match */
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
......
...@@ -445,7 +445,7 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream, ...@@ -445,7 +445,7 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream,
pages = snd_sgbuf_aligned_pages(size); pages = snd_sgbuf_aligned_pages(size);
dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n", dev_dbg(rtd->dev, "generating page table for %p size 0x%zx pages %d\n",
dma_area, size, pages); dma_area, size, pages);
for (i = 0; i < pages; i++) { for (i = 0; i < pages; i++) {
......
...@@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o ...@@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
# Skylake IPC Support # Skylake IPC Support
snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
skl-sst.o skl-sst.o bxt-sst.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
/*
* bxt-sst.c - DSP library functions for BXT platform
*
* Copyright (C) 2015-16 Intel Corp
* Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
* Jeeja KP <jeeja.kp@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 published by
* the Free Software Foundation; version 2 of the License.
*
* 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.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/device.h>
#include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h"
#include "skl-sst-ipc.h"
#define BXT_BASEFW_TIMEOUT 3000
#define BXT_INIT_TIMEOUT 500
#define BXT_IPC_PURGE_FW 0x01004000
#define BXT_ROM_INIT 0x5
#define BXT_ADSP_SRAM0_BASE 0x80000
/* Firmware status window */
#define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE
#define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4)
#define BXT_ADSP_SRAM1_BASE 0xA0000
static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
}
static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
const void *fwdata, u32 fwsize)
{
int stream_tag, ret, i;
u32 reg;
stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
if (stream_tag < 0) {
dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
stream_tag);
return stream_tag;
}
ctx->dsp_ops.stream_tag = stream_tag;
memcpy(ctx->dmab.area, fwdata, fwsize);
/* Purge FW request */
sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
BXT_IPC_PURGE_FW | (stream_tag - 1));
ret = skl_dsp_enable_core(ctx);
if (ret < 0) {
dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
ret = -EIO;
goto base_fw_load_failed;
}
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
if (reg & SKL_ADSP_REG_HIPCIE_DONE) {
sst_dsp_shim_update_bits_forced(ctx,
SKL_ADSP_REG_HIPCIE,
SKL_ADSP_REG_HIPCIE_DONE,
SKL_ADSP_REG_HIPCIE_DONE);
break;
}
mdelay(1);
}
if (!i) {
dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg);
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE,
SKL_ADSP_REG_HIPCIE_DONE,
SKL_ADSP_REG_HIPCIE_DONE);
}
/* enable Interrupt */
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
if (SKL_FW_INIT ==
(sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
SKL_FW_STS_MASK)) {
dev_info(ctx->dev, "ROM loaded, continue FW loading\n");
break;
}
mdelay(1);
}
if (!i) {
dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg);
ret = -EIO;
goto base_fw_load_failed;
}
return ret;
base_fw_load_failed:
ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
skl_dsp_disable_core(ctx);
return ret;
}
static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
{
int ret;
ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag);
ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK,
BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot");
ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag);
ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag);
return ret;
}
static int bxt_load_base_firmware(struct sst_dsp *ctx)
{
const struct firmware *fw = NULL;
struct skl_sst *skl = ctx->thread_context;
int ret;
ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
if (ret < 0) {
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
goto sst_load_base_firmware_failed;
}
ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
/* Retry Enabling core and ROM load. Retry seemed to help */
if (ret < 0) {
ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
if (ret < 0) {
dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
goto sst_load_base_firmware_failed;
}
}
ret = sst_transfer_fw_host_dma(ctx);
if (ret < 0) {
dev_err(ctx->dev, "Transfer firmware failed %d\n", ret);
dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
skl_dsp_disable_core(ctx);
} else {
dev_dbg(ctx->dev, "Firmware download successful\n");
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
if (ret == 0) {
dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n");
skl_dsp_disable_core(ctx);
ret = -EIO;
} else {
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
ret = 0;
}
}
sst_load_base_firmware_failed:
release_firmware(fw);
return ret;
}
static int bxt_set_dsp_D0(struct sst_dsp *ctx)
{
struct skl_sst *skl = ctx->thread_context;
int ret;
skl->boot_complete = false;
ret = skl_dsp_enable_core(ctx);
if (ret < 0) {
dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
return ret;
}
/* enable interrupt */
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
if (ret == 0) {
dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
return -EIO;
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
return 0;
}
static int bxt_set_dsp_D3(struct sst_dsp *ctx)
{
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
int ret = 0;
if (!is_skl_dsp_running(ctx))
return ret;
dx.core_mask = SKL_DSP_CORE0_MASK;
dx.dx_mask = SKL_IPC_D3_MASK;
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
SKL_BASE_FW_MODULE_ID, &dx);
if (ret < 0) {
dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret);
return ret;
}
ret = skl_dsp_disable_core(ctx);
if (ret < 0) {
dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
ret = -EIO;
}
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
return 0;
}
static struct skl_dsp_fw_ops bxt_fw_ops = {
.set_state_D0 = bxt_set_dsp_D0,
.set_state_D3 = bxt_set_dsp_D3,
.load_fw = bxt_load_base_firmware,
.get_fw_errcode = bxt_get_errorcode,
};
static struct sst_ops skl_ops = {
.irq_handler = skl_dsp_sst_interrupt,
.write = sst_shim32_write,
.read = sst_shim32_read,
.ram_read = sst_memcpy_fromio_32,
.ram_write = sst_memcpy_toio_32,
.free = skl_dsp_free,
};
static struct sst_dsp_device skl_dev = {
.thread = skl_dsp_irq_thread_handler,
.ops = &skl_ops,
};
int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
struct skl_sst **dsp)
{
struct skl_sst *skl;
struct sst_dsp *sst;
int ret;
skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
if (skl == NULL)
return -ENOMEM;
skl->dev = dev;
skl_dev.thread_context = skl;
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
dev_err(skl->dev, "skl_dsp_ctx_init failed\n");
return -ENODEV;
}
sst = skl->dsp;
sst->fw_name = fw_name;
sst->dsp_ops = dsp_ops;
sst->fw_ops = bxt_fw_ops;
sst->addr.lpe = mmio_base;
sst->addr.shim = mmio_base;
sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
ret = skl_ipc_init(dev, skl);
if (ret)
return ret;
skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait);
ret = sst->fw_ops.load_fw(sst);
if (ret < 0) {
dev_err(dev, "Load base fw failed: %x", ret);
return ret;
}
if (dsp)
*dsp = skl;
return 0;
}
EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
skl_ipc_free(&ctx->ipc);
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
if (ctx->dsp->addr.lpe)
iounmap(ctx->dsp->addr.lpe);
ctx->dsp->ops->free(ctx->dsp);
}
EXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel Broxton IPC driver");
...@@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, ...@@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
void *skl_nhlt_init(struct device *dev) struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
{ {
acpi_handle handle; acpi_handle handle;
union acpi_object *obj; union acpi_object *obj;
struct nhlt_resource_desc *nhlt_ptr = NULL; struct nhlt_resource_desc *nhlt_ptr = NULL;
struct nhlt_acpi_table *nhlt_table = NULL;
if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) { if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
dev_err(dev, "Requested NHLT device not found\n"); dev_err(dev, "Requested NHLT device not found\n");
...@@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev) ...@@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev)
obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL); obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL);
if (obj && obj->type == ACPI_TYPE_BUFFER) { if (obj && obj->type == ACPI_TYPE_BUFFER) {
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
nhlt_table = (struct nhlt_acpi_table *)
return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
MEMREMAP_WB); MEMREMAP_WB);
ACPI_FREE(obj);
return nhlt_table;
} }
dev_err(dev, "device specific method to extract NHLT blob failed\n"); dev_err(dev, "device specific method to extract NHLT blob failed\n");
return NULL; return NULL;
} }
void skl_nhlt_free(void *addr) void skl_nhlt_free(struct nhlt_acpi_table *nhlt)
{ {
memunmap(addr); memunmap((void *) nhlt);
} }
static struct nhlt_specific_cfg *skl_get_specific_cfg( static struct nhlt_specific_cfg *skl_get_specific_cfg(
...@@ -120,7 +123,7 @@ struct nhlt_specific_cfg ...@@ -120,7 +123,7 @@ struct nhlt_specific_cfg
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
struct device *dev = bus->dev; struct device *dev = bus->dev;
struct nhlt_specific_cfg *sp_config; struct nhlt_specific_cfg *sp_config;
struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; struct nhlt_acpi_table *nhlt = skl->nhlt;
u16 bps = (s_fmt == 16) ? 16 : 32; u16 bps = (s_fmt == 16) ? 16 : 32;
u8 j; u8 j;
......
此差异已折叠。
...@@ -336,8 +336,6 @@ void skl_dsp_free(struct sst_dsp *dsp) ...@@ -336,8 +336,6 @@ void skl_dsp_free(struct sst_dsp *dsp)
skl_ipc_int_disable(dsp); skl_ipc_int_disable(dsp);
free_irq(dsp->irq, dsp); free_irq(dsp->irq, dsp);
dsp->cl_dev.ops.cl_cleanup_controller(dsp);
skl_cldma_int_disable(dsp);
skl_ipc_op_int_disable(dsp); skl_ipc_op_int_disable(dsp);
skl_ipc_int_disable(dsp); skl_ipc_int_disable(dsp);
......
此差异已折叠。
...@@ -281,7 +281,7 @@ enum skl_module_state { ...@@ -281,7 +281,7 @@ enum skl_module_state {
}; };
struct skl_module_cfg { struct skl_module_cfg {
char guid[SKL_UUID_STR_SZ]; u8 guid[16];
struct skl_module_inst_id id; struct skl_module_inst_id id;
u8 domain; u8 domain;
bool homogenous_inputs; bool homogenous_inputs;
......
...@@ -181,7 +181,7 @@ struct skl_dfw_pipe { ...@@ -181,7 +181,7 @@ struct skl_dfw_pipe {
} __packed; } __packed;
struct skl_dfw_module { struct skl_dfw_module {
char uuid[SKL_UUID_STR_SZ]; u8 uuid[16];
u16 module_id; u16 module_id;
u16 instance_id; u16 instance_id;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册