mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
ASoC: Updates for v4.7
The updates this time around are almost all driver code: - Further slow progress on the topology code. - Substantial updates and improvements for the da7219, es8328, fsl-ssi Intel and rcar drivers. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJXOao7AAoJECTWi3JdVIfQ3EQH/1Z4nukvcOeZgVN/4K9b27t2 LYSyPH4+7XiDsi24UAyxZWls625t+1XRtolS0yHYY+IMObkeH/T+StTirDG4C1Mv 0uw/lEs5XmkSPFMad2fDcVXhf+D6EsvuLZ24qLKhoi8TyePv6GRvYapitE4dAI7Z bBwjT+f9r1qSMJvfCmqit8zDneDFMKd7oqPmBW6NpFri5/ksn1KUnd/zOGu2SlSd R01Oa2VbRDGj8/Zzu5MORvgLLucxTqtAFYeF3T52M5oc33IBWvbha4fk/BDOswbz H9S3vHyakmbZgXnnGMTp4qz0bxA76YaHzjtqgGUEMbigHTsB0PP5TtII3i5LkaY= =Zsr1 -----END PGP SIGNATURE----- Merge tag 'asoc-v4.7' into asoc-linus ASoC: Updates for v4.7 The updates this time around are almost all driver code: - Further slow progress on the topology code. - Substantial updates and improvements for the da7219, es8328, fsl-ssi Intel and rcar drivers. # gpg: Signature made Mon 16 May 2016 12:08:43 BST using RSA key ID 5D5487D0 # gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>" # gpg: aka "Mark Brown <broonie@debian.org>" # gpg: aka "Mark Brown <broonie@kernel.org>" # gpg: aka "Mark Brown <broonie@tardis.ed.ac.uk>" # gpg: aka "Mark Brown <broonie@linaro.org>" # gpg: aka "Mark Brown <Mark.Brown@linaro.org>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 3F25 68AA C269 98F9 E813 A1C5 C3F4 36CA 30F5 D8EB # Subkey fingerprint: ADE6 68AA 6757 18B5 9FE2 9FEA 24D6 8B72 5D54 87D0
This commit is contained in:
commit
c64f976208
51
Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
Normal file
51
Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
Normal file
@ -0,0 +1,51 @@
|
||||
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.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, contains "fsl,vf610-sai" or
|
||||
"fsl,imx6sx-sai".
|
||||
- compatible : Compatible list, contains "fsl,vf610-sai",
|
||||
"fsl,imx6sx-sai" or "fsl,imx6ul-sai"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
@ -48,6 +48,11 @@ Required properties:
|
||||
receive data by following their own bit clocks and
|
||||
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:
|
||||
- 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
|
||||
|
13
Documentation/devicetree/bindings/sound/pcm5102a.txt
Normal file
13
Documentation/devicetree/bindings/sound/pcm5102a.txt
Normal file
@ -0,0 +1,13 @@
|
||||
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
|
||||
M: Timur Tabi <timur@tabi.org>
|
||||
M: Nicolin Chen <nicoleotsuka@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: linuxppc-dev@lists.ozlabs.org
|
||||
S: Maintained
|
||||
|
@ -447,5 +447,11 @@
|
||||
#define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18)
|
||||
#define IMX6UL_GPR1_ENET_CLK_DIR (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 */
|
||||
|
@ -51,6 +51,16 @@ struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
|
||||
void *filter_data);
|
||||
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
|
||||
* @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)
|
||||
* requesting the DMA channel.
|
||||
* @chan_name: Custom channel name to use when requesting DMA channel.
|
||||
* @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 {
|
||||
dma_addr_t addr;
|
||||
@ -72,6 +83,7 @@ struct snd_dmaengine_dai_dma_data {
|
||||
void *filter_data;
|
||||
const char *chan_name;
|
||||
unsigned int fifo_size;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
void snd_dmaengine_pcm_set_config_from_dai_data(
|
||||
|
@ -14,6 +14,8 @@
|
||||
* @gtscap: gts capabilities pointer
|
||||
* @drsmcap: dma resume capabilities pointer
|
||||
* @hlink_list: link list of HDA links
|
||||
* @lock: lock for link mgmt
|
||||
* @cmd_dma_state: state of cmd DMAs: CORB and RIRB
|
||||
*/
|
||||
struct hdac_ext_bus {
|
||||
struct hdac_bus bus;
|
||||
@ -27,6 +29,9 @@ struct hdac_ext_bus {
|
||||
void __iomem *drsmcap;
|
||||
|
||||
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,
|
||||
@ -142,6 +147,9 @@ struct hdac_ext_link {
|
||||
void __iomem *ml_addr; /* link output stream reg pointer */
|
||||
u32 lcaps; /* link capablities */
|
||||
u16 lsdiid; /* link sdi identifier */
|
||||
|
||||
int ref_count;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
@ -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,
|
||||
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 */
|
||||
#define snd_hdac_updatel(addr, reg, mask, val) \
|
||||
writel(((readl(addr + reg) & ~(mask)) | (val)), \
|
||||
|
100
include/sound/hdmi-codec.h
Normal file
100
include/sound/hdmi-codec.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 @@
|
||||
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len);
|
||||
|
||||
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||
u8 *cs, size_t len);
|
||||
#endif
|
||||
|
@ -100,6 +100,7 @@ struct device;
|
||||
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \
|
||||
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
|
||||
.kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
|
||||
/* DEPRECATED: use SND_SOC_DAPM_SUPPLY */
|
||||
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
|
||||
{ .id = snd_soc_dapm_micbias, .name = wname, \
|
||||
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
|
||||
@ -473,7 +474,7 @@ enum snd_soc_dapm_type {
|
||||
snd_soc_dapm_out_drv, /* output driver */
|
||||
snd_soc_dapm_adc, /* analog to digital 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_hp, /* headphones */
|
||||
snd_soc_dapm_spk, /* speaker */
|
||||
|
@ -1002,7 +1002,7 @@ struct snd_soc_dai_link {
|
||||
*/
|
||||
const char *platform_name;
|
||||
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;
|
||||
unsigned int num_params;
|
||||
@ -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,
|
||||
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>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -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
|
||||
* 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
|
||||
* addr_width field of the DAI DMA data struct is not equal to
|
||||
* DMA_SLAVE_BUSWIDTH_UNDEFINED.
|
||||
* SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
|
||||
* 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(
|
||||
const struct snd_pcm_substream *substream,
|
||||
@ -117,11 +118,17 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
slave_config->dst_addr = dma_data->addr;
|
||||
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)
|
||||
slave_config->dst_addr_width = dma_data->addr_width;
|
||||
} else {
|
||||
slave_config->src_addr = dma_data->addr;
|
||||
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)
|
||||
slave_config->src_addr_width = dma_data->addr_width;
|
||||
}
|
||||
|
@ -9,30 +9,18 @@
|
||||
#include <linux/types.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm_iec958.h>
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static int create_iec958_consumer(uint rate, uint sample_width,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
unsigned int fs, ws;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
switch (runtime->rate) {
|
||||
switch (rate) {
|
||||
case 32000:
|
||||
fs = IEC958_AES3_CON_FS_32000;
|
||||
break;
|
||||
@ -59,7 +47,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
}
|
||||
|
||||
if (len > 4) {
|
||||
switch (snd_pcm_format_width(runtime->format)) {
|
||||
switch (sample_width) {
|
||||
case 16:
|
||||
ws = IEC958_AES4_CON_WORDLEN_20_16;
|
||||
break;
|
||||
@ -71,6 +59,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
IEC958_AES4_CON_MAX_WORDLEN_24;
|
||||
break;
|
||||
case 24:
|
||||
case 32: /* Assume 24-bit width for 32-bit samples. */
|
||||
ws = IEC958_AES4_CON_WORDLEN_24_20 |
|
||||
IEC958_AES4_CON_MAX_WORDLEN_24;
|
||||
break;
|
||||
@ -92,4 +81,46 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
INIT_LIST_HEAD(&ebus->hlink_list);
|
||||
ebus->idx = idx++;
|
||||
|
||||
mutex_init(&ebus->lock);
|
||||
ebus->cmd_dma_state = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
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)
|
||||
hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
|
||||
return 0;
|
||||
}
|
||||
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)
|
||||
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[];
|
||||
int hda_widget_sysfs_init(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,
|
||||
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
|
||||
| SSC_BF(RCMR_STTDLY, 1)
|
||||
| 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_CKS, SSC_CKS_DIV);
|
||||
|
||||
@ -692,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||
rcmr = SSC_BF(RCMR_PERIOD, 0)
|
||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||
| 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_CKS, ssc->clk_from_rk_pin ?
|
||||
SSC_CKS_PIN : SSC_CKS_CLOCK);
|
||||
|
@ -206,8 +206,8 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
stype = substream->stream;
|
||||
pcd = to_dmadata(substream);
|
||||
|
||||
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
|
||||
"runtime->min_align %d\n",
|
||||
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
|
||||
"runtime->min_align %lu\n",
|
||||
(unsigned long)runtime->dma_area,
|
||||
(unsigned long)runtime->dma_addr, runtime->dma_bytes,
|
||||
runtime->min_align);
|
||||
|
@ -259,6 +259,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
data_length = 16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
data_length = 24;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
data_length = 32;
|
||||
break;
|
||||
@ -273,13 +276,20 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
/* otherwise calculate a fitting block ratio */
|
||||
bclk_ratio = 2 * data_length;
|
||||
|
||||
/* set target clock rate*/
|
||||
clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
|
||||
/* Clock should only be set up here if CPU is clock master */
|
||||
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 */
|
||||
format = BCM2835_I2S_CHEN;
|
||||
|
||||
if (data_length > 24)
|
||||
if (data_length >= 24)
|
||||
format |= BCM2835_I2S_CHWEX;
|
||||
|
||||
format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
|
||||
@ -570,6 +580,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
|
||||
.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
|
||||
},
|
||||
.capture = {
|
||||
@ -577,6 +588,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
|
||||
.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
|
||||
},
|
||||
.ops = &bcm2835_i2s_dai_ops,
|
||||
@ -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_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 */
|
||||
dev->bclk_ratio = 0;
|
||||
|
||||
|
@ -88,12 +88,14 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_MC13783 if MFD_MC13XXX
|
||||
select SND_SOC_ML26124 if I2C
|
||||
select SND_SOC_NAU8825 if I2C
|
||||
select SND_SOC_HDMI_CODEC
|
||||
select SND_SOC_PCM1681 if I2C
|
||||
select SND_SOC_PCM179X_I2C if I2C
|
||||
select SND_SOC_PCM179X_SPI if SPI_MASTER
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_PCM3168A_I2C if I2C
|
||||
select SND_SOC_PCM3168A_SPI if SPI_MASTER
|
||||
select SND_SOC_PCM5102A
|
||||
select SND_SOC_PCM512x_I2C if I2C
|
||||
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
||||
select SND_SOC_RT286 if I2C
|
||||
@ -477,6 +479,11 @@ config SND_SOC_BT_SCO
|
||||
config SND_SOC_DMIC
|
||||
tristate
|
||||
|
||||
config SND_SOC_HDMI_CODEC
|
||||
tristate
|
||||
select SND_PCM_ELD
|
||||
select SND_PCM_IEC958
|
||||
|
||||
config SND_SOC_ES8328
|
||||
tristate "Everest Semi ES8328 CODEC"
|
||||
|
||||
@ -575,6 +582,9 @@ config SND_SOC_PCM3168A_SPI
|
||||
select SND_SOC_PCM3168A
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_PCM5102A
|
||||
tristate
|
||||
|
||||
config SND_SOC_PCM512x
|
||||
tristate
|
||||
|
||||
|
@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o
|
||||
snd-soc-mc13783-objs := mc13783.o
|
||||
snd-soc-ml26124-objs := ml26124.o
|
||||
snd-soc-nau8825-objs := nau8825.o
|
||||
snd-soc-hdmi-codec-objs := hdmi-codec.o
|
||||
snd-soc-pcm1681-objs := pcm1681.o
|
||||
snd-soc-pcm179x-codec-objs := pcm179x.o
|
||||
snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
|
||||
@ -89,6 +90,7 @@ snd-soc-pcm3008-objs := pcm3008.o
|
||||
snd-soc-pcm3168a-objs := pcm3168a.o
|
||||
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
|
||||
snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
|
||||
snd-soc-pcm5102a-objs := pcm5102a.o
|
||||
snd-soc-pcm512x-objs := pcm512x.o
|
||||
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.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_ML26124) += snd-soc-ml26124.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_PCM179X) += snd-soc-pcm179x-codec.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
|
||||
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_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_I2C) += snd-soc-pcm512x-i2c.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)
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &clk_name);
|
||||
|
||||
clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
|
||||
(parent_clk_name) ? 0 : CLK_IS_ROOT,
|
||||
rate);
|
||||
clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate);
|
||||
if (!IS_ERR(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)
|
||||
|
||||
switch (arizona->type) {
|
||||
case WM8997:
|
||||
case CS47L24:
|
||||
case WM1831:
|
||||
break;
|
||||
default:
|
||||
ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
|
||||
@ -1134,7 +1136,6 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
unsigned int mask = 0x3 << w->shift;
|
||||
unsigned int val;
|
||||
|
||||
switch (event) {
|
||||
@ -1148,7 +1149,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
|
||||
snd_soc_write(codec, ARIZONA_CLOCK_CONTROL, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2047,7 +2048,21 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
|
||||
init_ratio, Fref, 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++) {
|
||||
if ((ARIZONA_FLL_VCO_CORNER / 2) /
|
||||
(fll->vco_mult * ratio) < Fref) {
|
||||
@ -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;
|
||||
Fref /= 2;
|
||||
refdiv++;
|
||||
|
@ -56,7 +56,7 @@ struct cs42l56_private {
|
||||
u8 iface;
|
||||
u8 iface_fmt;
|
||||
u8 iface_inv;
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
#if IS_ENABLED(CONFIG_INPUT)
|
||||
struct input_dev *beep;
|
||||
struct work_struct beep_work;
|
||||
int beep_rate;
|
||||
|
@ -807,6 +807,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
|
||||
{ "IN2L PGA", NULL, "IN2L" },
|
||||
{ "IN2R PGA", NULL, "IN2R" },
|
||||
|
||||
{ "Audio Trace DSP", NULL, "DSP2" },
|
||||
{ "Audio Trace DSP", NULL, "SYSCLK" },
|
||||
|
||||
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
|
||||
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
|
||||
|
||||
@ -1016,6 +1019,27 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
|
||||
.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)
|
||||
@ -1027,6 +1051,8 @@ static int cs47l24_open(struct snd_compr_stream *stream)
|
||||
|
||||
if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
|
||||
n_adsp = 2;
|
||||
} else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) {
|
||||
n_adsp = 1;
|
||||
} else {
|
||||
dev_err(arizona->dev,
|
||||
"No suitable compressed stream for DAI '%s'\n",
|
||||
@ -1041,10 +1067,16 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
|
||||
{
|
||||
struct cs47l24_priv *priv = data;
|
||||
struct arizona *arizona = priv->core.arizona;
|
||||
int ret;
|
||||
int serviced = 0;
|
||||
int i, ret;
|
||||
|
||||
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
|
||||
if (ret == -ENODEV) {
|
||||
for (i = 1; i <= 2; ++i) {
|
||||
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");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
@ -1160,6 +1192,7 @@ static struct snd_compr_ops cs47l24_compr_ops = {
|
||||
static struct snd_soc_platform_driver cs47l24_compr_platform = {
|
||||
.compr_ops = &cs47l24_compr_ops,
|
||||
};
|
||||
|
||||
static int cs47l24_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||
@ -1228,9 +1261,9 @@ static int cs47l24_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
|
||||
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
@ -1241,10 +1274,15 @@ static int cs47l24_probe(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_codec(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
wm_adsp2_remove(&cs47l24->core.adsp[1]);
|
||||
wm_adsp2_remove(&cs47l24->core.adsp[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
@ -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 */
|
||||
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
|
||||
@ -1143,11 +1206,9 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
/* Set master/slave mode */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE;
|
||||
da7213->master = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE;
|
||||
da7213->master = false;
|
||||
break;
|
||||
default:
|
||||
@ -1281,28 +1342,28 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
||||
pll_ctrl = 0;
|
||||
|
||||
/* Workout input divider based on MCLK rate */
|
||||
if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) {
|
||||
if (da7213->mclk_rate == 32768) {
|
||||
/* 32KHz PLL Mode */
|
||||
indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
|
||||
indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
|
||||
freq_ref = 3750000;
|
||||
pll_ctrl |= DA7213_PLL_32K_MODE;
|
||||
} else {
|
||||
/* 5 - 54MHz MCLK */
|
||||
if (da7213->mclk_rate < 5000000) {
|
||||
goto pll_err;
|
||||
} else if (da7213->mclk_rate <= 10000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_5_10_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL;
|
||||
} else if (da7213->mclk_rate <= 20000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
|
||||
} else if (da7213->mclk_rate <= 40000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_20_40_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL;
|
||||
} else if (da7213->mclk_rate <= 9000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL;
|
||||
} else if (da7213->mclk_rate <= 18000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
|
||||
} else if (da7213->mclk_rate <= 36000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL;
|
||||
} else if (da7213->mclk_rate <= 54000000) {
|
||||
indiv_bits = DA7213_PLL_INDIV_40_54_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL;
|
||||
indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
|
||||
indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
|
||||
} else {
|
||||
goto pll_err;
|
||||
}
|
||||
@ -1547,6 +1608,10 @@ static int da7213_probe(struct snd_soc_codec *codec)
|
||||
/* Default to using SRM for slave mode */
|
||||
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 */
|
||||
snd_soc_update_bits(codec, DA7213_AUX_L_CTRL,
|
||||
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
|
||||
|
@ -142,6 +142,9 @@
|
||||
* Bit fields
|
||||
*/
|
||||
|
||||
/* DA7213_PLL_STATUS = 0x03 */
|
||||
#define DA7219_PLL_SRM_LOCK (0x1 << 1)
|
||||
|
||||
/* DA7213_SR = 0x22 */
|
||||
#define DA7213_SR_8000 (0x1 << 0)
|
||||
#define DA7213_SR_11025 (0x2 << 0)
|
||||
@ -160,10 +163,10 @@
|
||||
#define DA7213_VMID_EN (0x1 << 7)
|
||||
|
||||
/* DA7213_PLL_CTRL = 0x27 */
|
||||
#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2)
|
||||
#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2)
|
||||
#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2)
|
||||
#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2)
|
||||
#define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2)
|
||||
#define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2)
|
||||
#define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2)
|
||||
#define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2)
|
||||
#define DA7213_PLL_INDIV_MASK (0x3 << 2)
|
||||
#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4)
|
||||
#define DA7213_PLL_32K_MODE (0x1 << 5)
|
||||
@ -178,8 +181,6 @@
|
||||
#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
|
||||
#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
|
||||
#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)
|
||||
|
||||
/* DA7213_DAI_CTRL = 0x29 */
|
||||
@ -412,6 +413,9 @@
|
||||
#define DA7213_DMIC_CLK_RATE_SHIFT 2
|
||||
#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2)
|
||||
|
||||
/* DA7213_PC_COUNT = 0x94 */
|
||||
#define DA7213_PC_FREERUN_MASK (0x1 << 0)
|
||||
|
||||
/* DA7213_DIG_CTRL = 0x99 */
|
||||
#define DA7213_DAC_L_INV_SHIFT 3
|
||||
#define DA7213_DAC_R_INV_SHIFT 7
|
||||
@ -495,15 +499,16 @@
|
||||
#define DA7213_ALC_AVG_ITERATIONS 5
|
||||
|
||||
/* PLL related */
|
||||
#define DA7213_SYSCLK_MCLK 0
|
||||
#define DA7213_SYSCLK_PLL 1
|
||||
#define DA7213_PLL_FREQ_OUT_90316800 90316800
|
||||
#define DA7213_PLL_FREQ_OUT_98304000 98304000
|
||||
#define DA7213_PLL_FREQ_OUT_94310400 94310400
|
||||
#define DA7213_PLL_INDIV_5_10_MHZ_VAL 2
|
||||
#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4
|
||||
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
|
||||
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
|
||||
#define DA7213_SYSCLK_MCLK 0
|
||||
#define DA7213_SYSCLK_PLL 1
|
||||
#define DA7213_PLL_FREQ_OUT_90316800 90316800
|
||||
#define DA7213_PLL_FREQ_OUT_98304000 98304000
|
||||
#define DA7213_PLL_FREQ_OUT_94310400 94310400
|
||||
#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2
|
||||
#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4
|
||||
#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8
|
||||
#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16
|
||||
#define DA7213_SRM_CHECK_RETRIES 8
|
||||
|
||||
enum da7213_clk_src {
|
||||
DA7213_CLKSRC_MCLK = 0,
|
||||
|
@ -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 */
|
||||
if (da7218->mclk_rate == 32768) {
|
||||
indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
|
||||
indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate < 2000000) {
|
||||
dev_err(codec->dev, "PLL input clock %d below valid range\n",
|
||||
da7218->mclk_rate);
|
||||
return -EINVAL;
|
||||
} else if (da7218->mclk_rate <= 5000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 10000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_5_10_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 20000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_10_20_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 40000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_20_40_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 4500000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 9000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 18000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 36000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL;
|
||||
} else if (da7218->mclk_rate <= 54000000) {
|
||||
indiv_bits = DA7218_PLL_INDIV_40_54_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL;
|
||||
indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ;
|
||||
indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL;
|
||||
} else {
|
||||
dev_err(codec->dev, "PLL input clock %d above valid range\n",
|
||||
da7218->mclk_rate);
|
||||
|
@ -876,15 +876,11 @@
|
||||
/* DA7218_PLL_CTRL = 0x91 */
|
||||
#define DA7218_PLL_INDIV_SHIFT 0
|
||||
#define DA7218_PLL_INDIV_MASK (0x7 << 0)
|
||||
#define DA7218_PLL_INDIV_2_5_MHZ (0x0 << 0)
|
||||
#define DA7218_PLL_INDIV_5_10_MHZ (0x1 << 0)
|
||||
#define DA7218_PLL_INDIV_10_20_MHZ (0x2 << 0)
|
||||
#define DA7218_PLL_INDIV_20_40_MHZ (0x3 << 0)
|
||||
#define DA7218_PLL_INDIV_40_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_INDIV_2_TO_4_5_MHZ (0x0 << 0)
|
||||
#define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0)
|
||||
#define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0)
|
||||
#define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0)
|
||||
#define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0)
|
||||
#define DA7218_PLL_MCLK_SQR_EN_SHIFT 4
|
||||
#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4)
|
||||
#define DA7218_PLL_MODE_SHIFT 6
|
||||
@ -1336,6 +1332,13 @@
|
||||
#define DA7218_PLL_FREQ_OUT_90316 90316800
|
||||
#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 */
|
||||
#define DA7218_ALC_CALIB_DELAY_MIN 2500
|
||||
#define DA7218_ALC_CALIB_DELAY_MAX 5000
|
||||
|
@ -11,6 +11,7 @@
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -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))
|
||||
return 0;
|
||||
|
||||
if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
|
||||
if ((freq < 2000000) || (freq > 54000000)) {
|
||||
dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
|
||||
freq);
|
||||
return -EINVAL;
|
||||
@ -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",
|
||||
da7219->mclk_rate);
|
||||
return -EINVAL;
|
||||
} else if (da7219->mclk_rate <= 5000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 10000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 20000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 40000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 4500000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 9000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 18000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 36000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate <= 54000000) {
|
||||
indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
|
||||
indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL;
|
||||
} else {
|
||||
dev_err(codec->dev, "PLL input clock %d above valid range\n",
|
||||
da7219->mclk_rate);
|
||||
@ -1426,6 +1427,12 @@ static const struct of_device_id 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
|
||||
da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
|
||||
{
|
||||
@ -1955,6 +1962,7 @@ static struct i2c_driver da7219_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "da7219",
|
||||
.of_match_table = of_match_ptr(da7219_of_match),
|
||||
.acpi_match_table = ACPI_PTR(da7219_acpi_match),
|
||||
},
|
||||
.probe = da7219_i2c_probe,
|
||||
.remove = da7219_i2c_remove,
|
||||
|
@ -194,11 +194,11 @@
|
||||
/* DA7219_PLL_CTRL = 0x20 */
|
||||
#define DA7219_PLL_INDIV_SHIFT 2
|
||||
#define DA7219_PLL_INDIV_MASK (0x7 << 2)
|
||||
#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2)
|
||||
#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2)
|
||||
#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2)
|
||||
#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2)
|
||||
#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2)
|
||||
#define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2)
|
||||
#define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2)
|
||||
#define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2)
|
||||
#define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 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_MASK (0x1 << 5)
|
||||
#define DA7219_PLL_MODE_SHIFT 6
|
||||
@ -761,11 +761,11 @@
|
||||
#define DA7219_PLL_FREQ_OUT_98304 98304000
|
||||
|
||||
/* PLL Frequency Dividers */
|
||||
#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1
|
||||
#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2
|
||||
#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
|
||||
#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
|
||||
#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
|
||||
#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1
|
||||
#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2
|
||||
#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4
|
||||
#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8
|
||||
#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16
|
||||
|
||||
/* SRM */
|
||||
#define DA7219_SRM_CHECK_RETRIES 8
|
||||
|
@ -26,18 +26,30 @@
|
||||
#include <sound/tlv.h>
|
||||
#include "es8328.h"
|
||||
|
||||
#define ES8328_SYSCLK_RATE_1X 11289600
|
||||
#define ES8328_SYSCLK_RATE_2X 22579200
|
||||
static const unsigned int rates_12288[] = {
|
||||
8000, 12000, 16000, 24000, 32000, 48000, 96000,
|
||||
};
|
||||
|
||||
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
|
||||
static struct {
|
||||
int rate;
|
||||
u8 ratio;
|
||||
} mclk_ratios[] = {
|
||||
{ 8000, 9 },
|
||||
{11025, 7 },
|
||||
{22050, 4 },
|
||||
{44100, 2 },
|
||||
static const int ratios_12288[] = {
|
||||
10, 7, 6, 4, 3, 2, 0,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list constraints_12288 = {
|
||||
.count = ARRAY_SIZE(rates_12288),
|
||||
.list = rates_12288,
|
||||
};
|
||||
|
||||
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 */
|
||||
@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
|
||||
"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_11025)
|
||||
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
|
||||
SNDRV_PCM_RATE_16000 | \
|
||||
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 regmap *regmap;
|
||||
struct clk *clk;
|
||||
int playback_fs;
|
||||
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];
|
||||
};
|
||||
|
||||
@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
|
||||
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,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int clk_rate;
|
||||
int i;
|
||||
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)
|
||||
reg = ES8328_DACCONTROL2;
|
||||
else
|
||||
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) &&
|
||||
(clk_rate != ES8328_SYSCLK_RATE_2X)) {
|
||||
dev_err(codec->dev,
|
||||
"%s: clock is running at %d Hz, not %d or %d Hz\n",
|
||||
__func__, clk_rate,
|
||||
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
|
||||
if (i == es8328->sysclk_constraints->count) {
|
||||
dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
|
||||
params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* find master mode MCLK to sampling frequency ratio */
|
||||
ratio = mclk_ratios[0].rate;
|
||||
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
|
||||
if (params_rate(params) <= mclk_ratios[i].rate)
|
||||
ratio = mclk_ratios[i].ratio;
|
||||
ratio = es8328->mclk_ratios[i];
|
||||
snd_soc_update_bits(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MCLKDIV2,
|
||||
es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
es8328->playback_fs = params_rate(params);
|
||||
es8328_set_deemph(codec);
|
||||
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) {
|
||||
snd_soc_update_bits(codec, ES8328_DACCONTROL1,
|
||||
ES8328_DACCONTROL1_DACWL_MASK,
|
||||
wl << ES8328_DACCONTROL1_DACWL_SHIFT);
|
||||
|
||||
es8328->playback_fs = params_rate(params);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
|
||||
int clk_rate;
|
||||
u8 mode = ES8328_DACCONTROL1_DACWL_16;
|
||||
u8 dac_mode = 0;
|
||||
u8 adc_mode = 0;
|
||||
|
||||
/* set master/slave audio interface */
|
||||
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,
|
||||
/* interface format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
|
||||
dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
|
||||
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
|
||||
dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
|
||||
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
|
||||
dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
|
||||
adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -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)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_write(codec, ES8328_DACCONTROL1, mode);
|
||||
snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
|
||||
snd_soc_update_bits(codec, ES8328_DACCONTROL1,
|
||||
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 */
|
||||
clk_rate = clk_get_rate(es8328->clk);
|
||||
if (clk_rate == ES8328_SYSCLK_RATE_1X)
|
||||
snd_soc_write(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MSC);
|
||||
else
|
||||
snd_soc_write(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MCLKDIV2 |
|
||||
ES8328_MASTERMODE_MSC);
|
||||
snd_soc_update_bits(codec, ES8328_MASTERMODE,
|
||||
ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -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 = {
|
||||
.startup = es8328_startup,
|
||||
.hw_params = es8328_hw_params,
|
||||
.digital_mute = es8328_mute,
|
||||
.set_sysclk = es8328_set_sysclk,
|
||||
.set_fmt = es8328_set_dai_fmt,
|
||||
};
|
||||
|
||||
@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = {
|
||||
.formats = ES8328_FORMATS,
|
||||
},
|
||||
.ops = &es8328_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static int es8328_suspend(struct snd_soc_codec *codec)
|
||||
@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.max_register = ES8328_REG_MAX,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_rw = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(es8328_regmap_config);
|
||||
|
||||
|
@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
|
||||
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 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_SEQEN (1 << 3)
|
||||
#define ES8328_CONTROL1_SAMEFS (1 << 4)
|
||||
@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
#define ES8328_ADCCONTROL1 0x09
|
||||
#define ES8328_ADCCONTROL2 0x0a
|
||||
#define ES8328_ADCCONTROL3 0x0b
|
||||
|
||||
#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_RATEMASK (0x1f << 0)
|
||||
|
||||
@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
|
||||
#define ES8328_ADCCONTROL14 0x16
|
||||
|
||||
#define ES8328_DACCONTROL1 0x17
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
|
||||
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
|
||||
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
|
||||
#define ES8328_DACCONTROL1_DACWL_20 (1 << 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_DACWL_SHIFT 3
|
||||
#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
|
||||
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
|
||||
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include <sound/pcm_drm_eld.h>
|
||||
#include <sound/hda_chmap.h>
|
||||
#include "../../hda/local.h"
|
||||
#include "hdac_hdmi.h"
|
||||
|
||||
@ -60,11 +61,17 @@ struct hdac_hdmi_cvt {
|
||||
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 {
|
||||
bool monitor_present;
|
||||
bool eld_valid;
|
||||
int eld_size;
|
||||
char eld_buffer[ELD_MAX_SIZE];
|
||||
struct hdac_hdmi_parsed_eld info;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pin {
|
||||
@ -76,6 +83,10 @@ struct hdac_hdmi_pin {
|
||||
struct hdac_ext_device *edev;
|
||||
int repoll_count;
|
||||
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 {
|
||||
@ -100,8 +111,22 @@ struct hdac_hdmi_priv {
|
||||
int num_pin;
|
||||
int num_cvt;
|
||||
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)
|
||||
{
|
||||
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,
|
||||
int i;
|
||||
const u8 *eld_buf;
|
||||
u8 conn_type;
|
||||
int channels = 2;
|
||||
int channels, ca;
|
||||
|
||||
list_for_each_entry(pin, &hdmi->pin_list, head) {
|
||||
if (pin->nid == pin_nid)
|
||||
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;
|
||||
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) {
|
||||
case DRM_ELD_CONN_TYPE_HDMI:
|
||||
hdmi_audio_infoframe_init(&frame);
|
||||
|
||||
/* Default stereo for now */
|
||||
frame.channels = channels;
|
||||
frame.channel_allocation = ca;
|
||||
|
||||
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (ret < 0)
|
||||
@ -311,7 +341,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
||||
dp_ai.len = 0x1b;
|
||||
dp_ai.ver = 0x11 << 2;
|
||||
dp_ai.CC02_CT47 = channels - 1;
|
||||
dp_ai.CA = 0;
|
||||
dp_ai.CA = ca;
|
||||
|
||||
dip = (u8 *)&dp_ai;
|
||||
break;
|
||||
@ -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_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
struct hdac_ext_dma_params *dd;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
|
||||
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,
|
||||
dai_map->pin->nid);
|
||||
mutex_unlock(&pin->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -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,
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -647,10 +689,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
||||
static int
|
||||
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;
|
||||
|
||||
/* Only stereo supported as of now */
|
||||
cvt->params.channels_min = cvt->params.channels_max = 2;
|
||||
chans = get_wcaps(hdac, cvt->nid);
|
||||
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,
|
||||
&cvt->params.rates,
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct hdac_ext_device *edev = pin->edev;
|
||||
@ -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);
|
||||
}
|
||||
hdac_hdmi_parse_eld(edev, pin);
|
||||
|
||||
print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
|
||||
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)
|
||||
hdmi->num_pin++;
|
||||
|
||||
pin->edev = edev;
|
||||
mutex_init(&pin->lock);
|
||||
INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
|
||||
|
||||
return 0;
|
||||
@ -1342,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = {
|
||||
.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)
|
||||
{
|
||||
char jack_name[NAME_SIZE];
|
||||
@ -1351,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
|
||||
snd_soc_component_get_dapm(&codec->component);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pcm *pcm;
|
||||
struct snd_pcm *snd_pcm;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
pcm->pcm_id = device;
|
||||
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);
|
||||
|
||||
sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
|
||||
@ -1378,10 +1464,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(&codec->component);
|
||||
struct hdac_hdmi_pin *pin;
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -1475,19 +1569,83 @@ static struct snd_soc_codec_driver hdmi_hda_codec = {
|
||||
.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)
|
||||
{
|
||||
struct hdac_device *codec = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi_priv;
|
||||
struct snd_soc_dai_driver *hdmi_dais = NULL;
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int num_dais = 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);
|
||||
if (hdmi_priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
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);
|
||||
|
||||
@ -1516,8 +1674,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
||||
}
|
||||
|
||||
/* ASoC specific initialization */
|
||||
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
|
||||
hdmi_dais, num_dais);
|
||||
ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
|
||||
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)
|
||||
@ -1556,6 +1718,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
unsigned long timeout;
|
||||
struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
@ -1579,6 +1744,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
|
||||
return err;
|
||||
}
|
||||
|
||||
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
|
||||
snd_hdac_ext_bus_link_put(ebus, hlink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1587,6 +1755,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
@ -1595,6 +1765,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
|
||||
if (!bus)
|
||||
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);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
|
432
sound/soc/codecs/hdmi-codec.c
Normal file
432
sound/soc/codecs/hdmi-codec.c
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* 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);
|
69
sound/soc/codecs/pcm5102a.c
Normal file
69
sound/soc/codecs/pcm5102a.c
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -1132,6 +1133,17 @@ static const struct acpi_device_id 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,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -1184,11 +1196,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
/* enable jack combo mode on supported devices */
|
||||
acpiid = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (acpiid) {
|
||||
if (acpiid && acpiid->driver_data) {
|
||||
rt298->pdata = *(struct rt298_platform_data *)
|
||||
acpiid->driver_data;
|
||||
}
|
||||
|
||||
if (dmi_check_system(force_combo_jack_table)) {
|
||||
rt298->pdata.cbj_en = true;
|
||||
rt298->pdata.gpio2_en = false;
|
||||
}
|
||||
|
||||
/* VREF Charging */
|
||||
regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80);
|
||||
regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860);
|
||||
|
@ -3286,10 +3286,8 @@ static void rt5645_jack_detect_work(struct work_struct *work)
|
||||
if (btn_type == 0)/* button release */
|
||||
report = rt5645->jack_type;
|
||||
else {
|
||||
if (rt5645->pdata.jd_invert) {
|
||||
mod_timer(&rt5645->btn_check_timer,
|
||||
msecs_to_jiffies(100));
|
||||
}
|
||||
mod_timer(&rt5645->btn_check_timer,
|
||||
msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -3557,6 +3555,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
|
||||
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,
|
||||
if (rt5645->pdata.jd_invert) {
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
|
||||
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->rcclock_work, rt5645_rcclock_work);
|
||||
|
@ -2098,10 +2098,14 @@ static int wm5102_probe(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_codec(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
wm_adsp2_remove(&wm5102->core.adsp[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2437,10 +2437,16 @@ static int wm5110_probe(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_codec(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
for (i = 0; i < WM5110_NUM_ADSP; i++)
|
||||
wm_adsp2_remove(&wm5110->core.adsp[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,8 @@
|
||||
#define ADSP2_RAM_RDY_SHIFT 0
|
||||
#define ADSP2_RAM_RDY_WIDTH 1
|
||||
|
||||
#define ADSP_MAX_STD_CTRL_SIZE 512
|
||||
|
||||
struct wm_adsp_buf {
|
||||
struct list_head list;
|
||||
void *buf;
|
||||
@ -271,8 +273,11 @@ struct wm_adsp_buffer {
|
||||
__be32 words_written[2]; /* total words written (64 bit) */
|
||||
};
|
||||
|
||||
struct wm_adsp_compr;
|
||||
|
||||
struct wm_adsp_compr_buf {
|
||||
struct wm_adsp *dsp;
|
||||
struct wm_adsp_compr *compr;
|
||||
|
||||
struct wm_adsp_buffer_region *regions;
|
||||
u32 host_buf_ptr;
|
||||
@ -435,6 +440,7 @@ struct wm_coeff_ctl {
|
||||
size_t len;
|
||||
unsigned int set:1;
|
||||
struct snd_kcontrol *kcontrol;
|
||||
struct soc_bytes_ext bytes_ext;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
@ -711,10 +717,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
|
||||
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,
|
||||
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->count = ctl->len;
|
||||
@ -763,7 +776,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
|
||||
static int wm_coeff_put(struct snd_kcontrol *kctl,
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
@ -780,6 +795,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
|
||||
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,
|
||||
void *buf, size_t len)
|
||||
{
|
||||
@ -822,7 +860,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
|
||||
static int wm_coeff_get(struct snd_kcontrol *kctl,
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
@ -845,12 +885,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
|
||||
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 wm_adsp *dsp;
|
||||
struct wm_coeff_ctl *ctl;
|
||||
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)
|
||||
{
|
||||
struct snd_kcontrol_new *kcontrol;
|
||||
@ -868,19 +968,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
|
||||
kcontrol->info = wm_coeff_info;
|
||||
kcontrol->get = wm_coeff_get;
|
||||
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) {
|
||||
if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
|
||||
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
|
||||
if (ctl->flags & WMFW_CTL_FLAG_READABLE)
|
||||
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
|
||||
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;
|
||||
}
|
||||
ctl->bytes_ext.max = ctl->len;
|
||||
ctl->bytes_ext.get = wm_coeff_tlv_get;
|
||||
ctl->bytes_ext.put = wm_coeff_tlv_put;
|
||||
|
||||
kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
|
||||
|
||||
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
|
||||
if (ret < 0)
|
||||
@ -944,6 +1040,13 @@ static void wm_adsp_ctl_work(struct work_struct *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,
|
||||
const struct wm_adsp_alg_region *alg_region,
|
||||
unsigned int offset, unsigned int len,
|
||||
@ -1032,11 +1135,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
|
||||
|
||||
ctl->flags = flags;
|
||||
ctl->offset = offset;
|
||||
if (len > 512) {
|
||||
adsp_warn(dsp, "Truncating control %s from %d\n",
|
||||
ctl->name, len);
|
||||
len = 512;
|
||||
}
|
||||
ctl->len = len;
|
||||
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
|
||||
if (!ctl->cache) {
|
||||
@ -1564,6 +1662,19 @@ static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
|
||||
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)
|
||||
{
|
||||
struct wmfw_adsp1_id_hdr adsp1_id;
|
||||
@ -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 wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm_adsp *dsp = &dsps[w->shift];
|
||||
struct wm_adsp_alg_region *alg_region;
|
||||
struct wm_coeff_ctl *ctl;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
@ -2074,13 +2184,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
|
||||
list_for_each_entry(ctl, &dsp->ctl_list, list)
|
||||
ctl->enabled = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
wm_adsp_free_alg_regions(dsp);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -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 wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm_adsp *dsp = &dsps[w->shift];
|
||||
struct wm_adsp_alg_region *alg_region;
|
||||
struct wm_coeff_ctl *ctl;
|
||||
int ret;
|
||||
|
||||
@ -2240,9 +2344,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
mutex_lock(&dsp->pwr_lock);
|
||||
|
||||
if (wm_adsp_fw[dsp->fw].num_caps != 0)
|
||||
ret = wm_adsp_buffer_init(dsp);
|
||||
|
||||
mutex_unlock(&dsp->pwr_lock);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
@ -2269,13 +2377,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||
list_for_each_entry(ctl, &dsp->ctl_list, list)
|
||||
ctl->enabled = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
wm_adsp_free_alg_regions(dsp);
|
||||
|
||||
if (wm_adsp_fw[dsp->fw].num_caps != 0)
|
||||
wm_adsp_buffer_free(dsp);
|
||||
@ -2340,6 +2442,54 @@ int wm_adsp2_init(struct wm_adsp *dsp)
|
||||
}
|
||||
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)
|
||||
{
|
||||
struct wm_adsp_compr *compr;
|
||||
@ -2393,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
|
||||
|
||||
mutex_lock(&dsp->pwr_lock);
|
||||
|
||||
wm_adsp_compr_detach(compr);
|
||||
dsp->compr = NULL;
|
||||
|
||||
kfree(compr->raw_buf);
|
||||
@ -2689,6 +2840,8 @@ err_buffer:
|
||||
static int wm_adsp_buffer_free(struct wm_adsp *dsp)
|
||||
{
|
||||
if (dsp->buffer) {
|
||||
wm_adsp_compr_detach(dsp->buffer->compr);
|
||||
|
||||
kfree(dsp->buffer->regions);
|
||||
kfree(dsp->buffer);
|
||||
|
||||
@ -2698,25 +2851,6 @@ static int wm_adsp_buffer_free(struct wm_adsp *dsp)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
avail += wm_adsp_buffer_size(buf);
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
struct wm_adsp_compr_buf *buf = dsp->buffer;
|
||||
struct wm_adsp_compr *compr = dsp->compr;
|
||||
struct wm_adsp_compr_buf *buf;
|
||||
struct wm_adsp_compr *compr;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dsp->pwr_lock);
|
||||
|
||||
buf = dsp->buffer;
|
||||
compr = dsp->compr;
|
||||
|
||||
if (!buf) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
@ -2827,16 +2981,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
|
||||
|
||||
adsp_dbg(dsp, "Handling buffer IRQ\n");
|
||||
|
||||
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
|
||||
if (ret < 0) {
|
||||
adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
if (buf->error != 0) {
|
||||
adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ret = wm_adsp_buffer_get_error(buf);
|
||||
if (ret < 0)
|
||||
goto out_notify; /* Wake poll to report error */
|
||||
|
||||
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
|
||||
&buf->irq_count);
|
||||
@ -2851,6 +2998,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_notify:
|
||||
if (compr && compr->stream)
|
||||
snd_compr_fragment_elapsed(compr->stream);
|
||||
|
||||
@ -2879,14 +3027,16 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
|
||||
struct snd_compr_tstamp *tstamp)
|
||||
{
|
||||
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_compr_buf *buf;
|
||||
int ret = 0;
|
||||
|
||||
adsp_dbg(dsp, "Pointer request\n");
|
||||
|
||||
mutex_lock(&dsp->pwr_lock);
|
||||
|
||||
buf = compr->buf;
|
||||
|
||||
if (!compr->buf) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
@ -2909,6 +3059,10 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
|
||||
* DSP to inform us once a whole fragment is available.
|
||||
*/
|
||||
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);
|
||||
if (ret < 0) {
|
||||
adsp_err(dsp,
|
||||
|
@ -92,6 +92,7 @@ extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
|
||||
|
||||
int wm_adsp1_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_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
|
||||
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
|
||||
|
@ -16,7 +16,11 @@ config SND_EDMA_SOC
|
||||
- DRA7xx family
|
||||
|
||||
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
|
||||
tristate "Multichannel Audio Serial Port (McASP) support"
|
||||
|
@ -4,9 +4,15 @@
|
||||
* Author: Vladimir Barinov, <vbarinov@embeddedalley.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
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* TODO:
|
||||
* on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
@ -650,13 +656,24 @@ static const struct snd_soc_component_driver davinci_i2s_component = {
|
||||
|
||||
static int davinci_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
struct davinci_mcbsp_dev *dev;
|
||||
struct resource *mem, *res;
|
||||
void __iomem *io_base;
|
||||
int *dma;
|
||||
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);
|
||||
if (IS_ERR(io_base))
|
||||
return PTR_ERR(io_base);
|
||||
@ -666,40 +683,44 @@ static int davinci_i2s_probe(struct platform_device *pdev)
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->base = io_base;
|
||||
|
||||
/* setup DMA, first TX, then RX */
|
||||
dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (res) {
|
||||
dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
*dma = res->start;
|
||||
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_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
|
||||
dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (res) {
|
||||
dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
|
||||
*dma = res->start;
|
||||
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;
|
||||
}
|
||||
|
||||
dev->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dev->clk))
|
||||
return -ENODEV;
|
||||
clk_enable(dev->clk);
|
||||
|
||||
dev->base = io_base;
|
||||
|
||||
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].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);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no DMA resource\n");
|
||||
ret = -ENXIO;
|
||||
goto err_release_clk;
|
||||
}
|
||||
dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
*dma = res->start;
|
||||
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no DMA resource\n");
|
||||
ret = -ENXIO;
|
||||
goto err_release_clk;
|
||||
}
|
||||
dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
|
||||
*dma = res->start;
|
||||
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma;
|
||||
|
||||
dev->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, dev);
|
||||
|
||||
@ -737,11 +758,18 @@ static int davinci_i2s_remove(struct platform_device *pdev)
|
||||
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 = {
|
||||
.probe = davinci_i2s_probe,
|
||||
.remove = davinci_i2s_remove,
|
||||
.driver = {
|
||||
.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,
|
||||
mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
|
||||
|
||||
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
|
||||
ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
|
||||
ACLKX | AFSX | ACLKR | AHCLKR | AFSR);
|
||||
mcasp->bclk_master = 0;
|
||||
break;
|
||||
default:
|
||||
@ -540,21 +540,19 @@ out:
|
||||
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)
|
||||
{
|
||||
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
pm_runtime_get_sync(mcasp->dev);
|
||||
switch (div_id) {
|
||||
case 0: /* MCLK divider */
|
||||
case MCASP_CLKDIV_AUXCLK: /* MCLK divider */
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
|
||||
AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
|
||||
AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
|
||||
break;
|
||||
|
||||
case 1: /* BCLK divider */
|
||||
case MCASP_CLKDIV_BCLK: /* BCLK divider */
|
||||
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
|
||||
ACLKXDIV(div - 1), ACLKXDIV_MASK);
|
||||
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,
|
||||
mcasp->bclk_div = div;
|
||||
break;
|
||||
|
||||
case 2: /*
|
||||
case MCASP_CLKDIV_BCLK_FS_RATIO:
|
||||
/*
|
||||
* BCLK/LRCLK ratio descries how many bit-clock cycles
|
||||
* fit into one frame. The clock ratio is given for a
|
||||
* 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,
|
||||
static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
|
||||
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,
|
||||
@ -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,
|
||||
unsigned int bclk_freq,
|
||||
int *error_ppm)
|
||||
unsigned int bclk_freq, bool set)
|
||||
{
|
||||
int div = mcasp->sysclk_freq / bclk_freq;
|
||||
int rem = mcasp->sysclk_freq % bclk_freq;
|
||||
int error_ppm;
|
||||
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 (div == 0 ||
|
||||
((mcasp->sysclk_freq / div) - bclk_freq) >
|
||||
(bclk_freq - (mcasp->sysclk_freq / (div+1)))) {
|
||||
((sysclk_freq / div) - bclk_freq) >
|
||||
(bclk_freq - (sysclk_freq / (div+1)))) {
|
||||
div++;
|
||||
rem = rem - bclk_freq;
|
||||
}
|
||||
}
|
||||
if (error_ppm)
|
||||
*error_ppm =
|
||||
(div*1000000 + (int)div64_long(1000000LL*rem,
|
||||
(int)bclk_freq))
|
||||
/div - 1000000;
|
||||
error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem,
|
||||
(int)bclk_freq)) / div - 1000000;
|
||||
|
||||
return div;
|
||||
if (set) {
|
||||
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 error_ppm;
|
||||
}
|
||||
|
||||
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 rate = params_rate(params);
|
||||
int sbits = params_width(params);
|
||||
int ppm, div;
|
||||
|
||||
if (mcasp->slot_width)
|
||||
sbits = mcasp->slot_width;
|
||||
|
||||
div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
|
||||
&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);
|
||||
davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true);
|
||||
}
|
||||
|
||||
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,
|
||||
davinci_mcasp_dai_rates[i];
|
||||
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 (range.empty) {
|
||||
range.min = davinci_mcasp_dai_rates[i];
|
||||
@ -1205,8 +1226,9 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
|
||||
if (rd->mcasp->slot_width)
|
||||
sbits = rd->mcasp->slot_width;
|
||||
|
||||
davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
|
||||
&ppm);
|
||||
ppm = davinci_mcasp_calc_clk_div(rd->mcasp,
|
||||
sbits * slots * rate,
|
||||
false);
|
||||
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
|
||||
snd_mask_set(&nfmt, i);
|
||||
count++;
|
||||
@ -1230,11 +1252,15 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
|
||||
int i, dir;
|
||||
int tdm_slots = mcasp->tdm_slots;
|
||||
|
||||
if (mcasp->tdm_mask[substream->stream])
|
||||
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
|
||||
/* Do not allow more then one stream per direction */
|
||||
if (mcasp->substreams[substream->stream])
|
||||
return -EBUSY;
|
||||
|
||||
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)
|
||||
return 0;
|
||||
|
||||
|
@ -306,4 +306,9 @@
|
||||
#define NUMEVT(x) (((x) & 0xFF) << 8)
|
||||
#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 */
|
||||
|
@ -100,6 +100,7 @@ struct dw_i2s_dev {
|
||||
struct device *dev;
|
||||
u32 ccr;
|
||||
u32 xfer_resolution;
|
||||
u32 fifo_th;
|
||||
|
||||
/* data related to DMA transfers b/w i2s and DMAC */
|
||||
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)
|
||||
static void i2s_start(struct dw_i2s_dev *dev,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct i2s_clk_config_data *config = &dev->config;
|
||||
u32 i, irq;
|
||||
i2s_write_reg(dev->i2s_base, IER, 1);
|
||||
|
||||
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));
|
||||
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
|
||||
}
|
||||
i2s_write_reg(dev->i2s_base, ITER, 1);
|
||||
} 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));
|
||||
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)
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
i2s_write_reg(dev->i2s_base, TCR(ch_reg),
|
||||
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));
|
||||
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
|
||||
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
|
||||
} else {
|
||||
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
|
||||
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));
|
||||
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
|
||||
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
|
||||
@ -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 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
|
||||
u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
|
||||
u32 idx;
|
||||
|
||||
if (dev->capability & DWC_I2S_RECORD &&
|
||||
@ -536,6 +541,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
|
||||
dev->capability |= DW_I2S_SLAVE;
|
||||
}
|
||||
|
||||
dev->fifo_th = fifo_depth / 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
|
||||
#include "fsl_sai.h"
|
||||
#include "imx-pcm.h"
|
||||
@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct fsl_sai *sai;
|
||||
struct regmap *gpr;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
char tmp[8];
|
||||
int irq, ret, i;
|
||||
int index;
|
||||
|
||||
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
|
||||
if (!sai)
|
||||
@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *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->is_lsb_first = of_property_read_bool(np, "lsb-first");
|
||||
@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
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_tx.addr = res->start + FSL_SAI_TDR;
|
||||
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
|
||||
@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
static const struct of_device_id fsl_sai_ids[] = {
|
||||
{ .compatible = "fsl,vf610-sai", },
|
||||
{ .compatible = "fsl,imx6sx-sai", },
|
||||
{ .compatible = "fsl,imx6ul-sai", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
|
||||
|
@ -137,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
|
||||
case CCSR_SSI_SACDAT:
|
||||
case CCSR_SSI_SATAG:
|
||||
case CCSR_SSI_SACCST:
|
||||
case CCSR_SSI_SOR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -261,6 +262,7 @@ struct fsl_ssi_private {
|
||||
struct fsl_ssi_dbg dbg_stats;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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,
|
||||
* (online configuration)
|
||||
*/
|
||||
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_STCR, vals->stcr, vals->stcr);
|
||||
regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
|
||||
} else {
|
||||
u32 sier;
|
||||
u32 srcr;
|
||||
@ -506,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
|
||||
|
||||
config_done:
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -670,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
|
||||
if (IS_ERR(ssi_private->baudclk))
|
||||
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));
|
||||
|
||||
/* 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,
|
||||
else
|
||||
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;
|
||||
afreq = clkrate / (i + 1);
|
||||
|
||||
@ -1158,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
|
||||
.playback = {
|
||||
.stream_name = "CPU-Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.channels_max = 32,
|
||||
.rates = FSLSSI_I2S_RATES,
|
||||
.formats = FSLSSI_I2S_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "CPU-Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.channels_max = 32,
|
||||
.rates = FSLSSI_I2S_RATES,
|
||||
.formats = FSLSSI_I2S_FORMATS,
|
||||
},
|
||||
@ -1402,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ssi_private->soc = of_id->data;
|
||||
ssi_private->dev = &pdev->dev;
|
||||
|
||||
sprop = of_get_property(np, "fsl,mode", NULL);
|
||||
if (sprop) {
|
||||
|
@ -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,
|
||||
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_addr,
|
||||
runtime->dma_bytes);
|
||||
|
@ -58,6 +58,21 @@ config SND_SOC_INTEL_HASWELL_MACH
|
||||
Say Y if you have such a device
|
||||
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
|
||||
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
@ -162,6 +177,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
|
||||
config SND_SOC_INTEL_SKYLAKE
|
||||
tristate
|
||||
select SND_HDA_EXT_CORE
|
||||
select SND_HDA_DSP_LOADER
|
||||
select SND_SOC_TOPOLOGY
|
||||
select SND_SOC_INTEL_SST
|
||||
|
||||
|
@ -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)
|
||||
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",
|
||||
kcontrol->id.name);
|
||||
return ret;
|
||||
|
@ -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-max98090-mach-objs := byt-max98090.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-rt5651-objs := bytcr_rt5651.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
|
||||
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_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_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.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[] = {
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "Codec",
|
||||
.be_id = 0,
|
||||
.id = 0,
|
||||
.cpu_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "snd-soc-dummy",
|
||||
.no_pcm = 1,
|
||||
|
353
sound/soc/intel/boards/bxt_rt298.c
Normal file
353
sound/soc/intel/boards/bxt_rt298.c
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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[] = {
|
||||
/* back ends */
|
||||
{
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
|
@ -267,7 +267,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
|
||||
/* back ends */
|
||||
{
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
|
@ -255,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
||||
/* back ends */
|
||||
{
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
|
@ -295,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
||||
/* back ends */
|
||||
{
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
|
@ -273,7 +273,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
||||
{
|
||||
/* SSP2 - Codec */
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
|
@ -156,7 +156,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "Codec",
|
||||
.be_id = 0,
|
||||
.id = 0,
|
||||
.cpu_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "snd-soc-dummy",
|
||||
.no_pcm = 1,
|
||||
|
@ -30,6 +30,16 @@
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
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 {
|
||||
SKL_DPCM_AUDIO_PB = 0,
|
||||
SKL_DPCM_AUDIO_CP,
|
||||
@ -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)
|
||||
{
|
||||
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
|
||||
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 0;
|
||||
}
|
||||
|
||||
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 skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
|
||||
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;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
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)
|
||||
@ -391,7 +434,6 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylaye_refcap_ops,
|
||||
@ -456,7 +498,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
@ -472,7 +514,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
{
|
||||
/* SSP1 - Codec */
|
||||
.name = "SSP1-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "SSP1 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
@ -489,7 +531,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 2,
|
||||
.id = 2,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
@ -501,7 +543,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp1",
|
||||
.be_id = 3,
|
||||
.id = 3,
|
||||
.cpu_dai_name = "iDisp1 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
@ -512,7 +554,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp2",
|
||||
.be_id = 4,
|
||||
.id = 4,
|
||||
.cpu_dai_name = "iDisp2 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi2",
|
||||
@ -523,7 +565,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp3",
|
||||
.be_id = 5,
|
||||
.id = 5,
|
||||
.cpu_dai_name = "iDisp3 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi3",
|
||||
@ -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 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825max",
|
||||
@ -547,11 +604,21 @@ static struct snd_soc_card skylake_audio_card = {
|
||||
.dapm_routes = skylake_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_map),
|
||||
.fully_routed = true,
|
||||
.late_probe = skylake_card_late_probe,
|
||||
};
|
||||
|
||||
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;
|
||||
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
@ -34,6 +34,15 @@
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
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 {
|
||||
SKL_DPCM_AUDIO_PB = 0,
|
||||
SKL_DPCM_AUDIO_CP,
|
||||
@ -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)
|
||||
{
|
||||
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
|
||||
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 0;
|
||||
}
|
||||
|
||||
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 skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
|
||||
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;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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 skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
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)
|
||||
@ -440,7 +482,6 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylaye_refcap_ops,
|
||||
@ -505,7 +546,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
@ -523,7 +564,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
{
|
||||
/* SSP1 - Codec */
|
||||
.name = "SSP1-Codec",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "SSP1 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
@ -540,7 +581,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 2,
|
||||
.id = 2,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
@ -552,7 +593,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp1",
|
||||
.be_id = 3,
|
||||
.id = 3,
|
||||
.cpu_dai_name = "iDisp1 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
@ -563,7 +604,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp2",
|
||||
.be_id = 4,
|
||||
.id = 4,
|
||||
.cpu_dai_name = "iDisp2 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi2",
|
||||
@ -574,7 +615,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp3",
|
||||
.be_id = 5,
|
||||
.id = 5,
|
||||
.cpu_dai_name = "iDisp3 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi3",
|
||||
@ -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 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825adi",
|
||||
@ -600,11 +656,21 @@ static struct snd_soc_card skylake_audio_card = {
|
||||
.codec_conf = ssm4567_codec_conf,
|
||||
.num_configs = ARRAY_SIZE(ssm4567_codec_conf),
|
||||
.fully_routed = true,
|
||||
.late_probe = skylake_card_late_probe,
|
||||
};
|
||||
|
||||
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;
|
||||
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
@ -30,6 +30,16 @@
|
||||
|
||||
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 {
|
||||
SKL_DPCM_AUDIO_PB = 0,
|
||||
SKL_DPCM_AUDIO_CP,
|
||||
@ -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)
|
||||
{
|
||||
struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
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[] = {
|
||||
@ -317,7 +338,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
@ -375,7 +395,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
@ -393,7 +413,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 1,
|
||||
.id = 1,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
@ -405,7 +425,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp1",
|
||||
.be_id = 2,
|
||||
.id = 2,
|
||||
.cpu_dai_name = "iDisp1 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
@ -416,7 +436,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp2",
|
||||
.be_id = 3,
|
||||
.id = 3,
|
||||
.cpu_dai_name = "iDisp2 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi2",
|
||||
@ -427,7 +447,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
},
|
||||
{
|
||||
.name = "iDisp3",
|
||||
.be_id = 4,
|
||||
.id = 4,
|
||||
.cpu_dai_name = "iDisp3 Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi3",
|
||||
@ -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 */
|
||||
static struct snd_soc_card skylake_rt286 = {
|
||||
.name = "skylake-rt286",
|
||||
@ -451,11 +486,21 @@ static struct snd_soc_card skylake_rt286 = {
|
||||
.dapm_routes = skylake_rt286_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
|
||||
.fully_routed = true,
|
||||
.late_probe = skylake_card_late_probe,
|
||||
};
|
||||
|
||||
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;
|
||||
snd_soc_card_set_drvdata(&skylake_rt286, ctx);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
|
||||
}
|
||||
|
@ -12,10 +12,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
/* 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]);
|
||||
#else
|
||||
inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* acpi match */
|
||||
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,
|
||||
|
||||
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);
|
||||
|
||||
for (i = 0; i < pages; i++) {
|
||||
|
@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
|
||||
|
||||
# Skylake IPC Support
|
||||
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
|
||||
|
328
sound/soc/intel/skylake/bxt-sst.c
Normal file
328
sound/soc/intel/skylake/bxt-sst.c
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* 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");
|
@ -72,6 +72,105 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
|
||||
skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
|
||||
}
|
||||
|
||||
static int skl_dsp_setup_spib(struct device *dev, unsigned int size,
|
||||
int stream_tag, int enable)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct hdac_stream *stream = snd_hdac_get_stream(bus,
|
||||
SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
|
||||
struct hdac_ext_stream *estream;
|
||||
|
||||
if (!stream)
|
||||
return -EINVAL;
|
||||
|
||||
estream = stream_to_hdac_ext_stream(stream);
|
||||
/* enable/disable SPIB for this hdac stream */
|
||||
snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index);
|
||||
|
||||
/* set the spib value */
|
||||
snd_hdac_ext_stream_set_spib(ebus, estream, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_dsp_prepare(struct device *dev, unsigned int format,
|
||||
unsigned int size, struct snd_dma_buffer *dmab)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct hdac_ext_stream *estream;
|
||||
struct hdac_stream *stream;
|
||||
struct snd_pcm_substream substream;
|
||||
int ret;
|
||||
|
||||
if (!bus)
|
||||
return -ENODEV;
|
||||
|
||||
memset(&substream, 0, sizeof(substream));
|
||||
substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
estream = snd_hdac_ext_stream_assign(ebus, &substream,
|
||||
HDAC_EXT_STREAM_TYPE_HOST);
|
||||
if (!estream)
|
||||
return -ENODEV;
|
||||
|
||||
stream = hdac_stream(estream);
|
||||
|
||||
/* assign decouple host dma channel */
|
||||
ret = snd_hdac_dsp_prepare(stream, format, size, dmab);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
skl_dsp_setup_spib(dev, size, stream->stream_tag, true);
|
||||
|
||||
return stream->stream_tag;
|
||||
}
|
||||
|
||||
static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
|
||||
struct hdac_stream *stream;
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
if (!bus)
|
||||
return -ENODEV;
|
||||
|
||||
stream = snd_hdac_get_stream(bus,
|
||||
SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
|
||||
if (!stream)
|
||||
return -EINVAL;
|
||||
|
||||
snd_hdac_dsp_trigger(stream, start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_dsp_cleanup(struct device *dev,
|
||||
struct snd_dma_buffer *dmab, int stream_tag)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
|
||||
struct hdac_stream *stream;
|
||||
struct hdac_ext_stream *estream;
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
if (!bus)
|
||||
return -ENODEV;
|
||||
|
||||
stream = snd_hdac_get_stream(bus,
|
||||
SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
|
||||
if (!stream)
|
||||
return -EINVAL;
|
||||
|
||||
estream = stream_to_hdac_ext_stream(stream);
|
||||
skl_dsp_setup_spib(dev, 0, stream_tag, false);
|
||||
snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
|
||||
|
||||
snd_hdac_dsp_cleanup(stream, dmab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct skl_dsp_loader_ops skl_get_loader_ops(void)
|
||||
{
|
||||
struct skl_dsp_loader_ops loader_ops;
|
||||
@ -84,6 +183,21 @@ static struct skl_dsp_loader_ops skl_get_loader_ops(void)
|
||||
return loader_ops;
|
||||
};
|
||||
|
||||
static struct skl_dsp_loader_ops bxt_get_loader_ops(void)
|
||||
{
|
||||
struct skl_dsp_loader_ops loader_ops;
|
||||
|
||||
memset(&loader_ops, 0, sizeof(loader_ops));
|
||||
|
||||
loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
|
||||
loader_ops.free_dma_buf = skl_free_dma_buf;
|
||||
loader_ops.prepare = skl_dsp_prepare;
|
||||
loader_ops.trigger = skl_dsp_trigger;
|
||||
loader_ops.cleanup = skl_dsp_cleanup;
|
||||
|
||||
return loader_ops;
|
||||
};
|
||||
|
||||
static const struct skl_dsp_ops dsp_ops[] = {
|
||||
{
|
||||
.id = 0x9d70,
|
||||
@ -91,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = {
|
||||
.init = skl_sst_dsp_init,
|
||||
.cleanup = skl_sst_dsp_cleanup
|
||||
},
|
||||
{
|
||||
.id = 0x5a98,
|
||||
.loader_ops = bxt_get_loader_ops,
|
||||
.init = bxt_sst_dsp_init,
|
||||
.cleanup = bxt_sst_dsp_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
static int skl_get_dsp_ops(int pci_id)
|
||||
@ -744,7 +864,7 @@ int skl_init_module(struct skl_sst *ctx,
|
||||
return ret;
|
||||
}
|
||||
mconfig->m_state = SKL_MODULE_INIT_DONE;
|
||||
|
||||
kfree(param_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
|
||||
|
||||
#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;
|
||||
union acpi_object *obj;
|
||||
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))) {
|
||||
dev_err(dev, "Requested NHLT device not found\n");
|
||||
@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev)
|
||||
obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL);
|
||||
if (obj && obj->type == ACPI_TYPE_BUFFER) {
|
||||
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
|
||||
|
||||
return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
|
||||
nhlt_table = (struct nhlt_acpi_table *)
|
||||
memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
|
||||
MEMREMAP_WB);
|
||||
ACPI_FREE(obj);
|
||||
return nhlt_table;
|
||||
}
|
||||
|
||||
dev_err(dev, "device specific method to extract NHLT blob failed\n");
|
||||
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(
|
||||
@ -120,7 +123,7 @@ struct nhlt_specific_cfg
|
||||
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
|
||||
struct device *dev = bus->dev;
|
||||
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;
|
||||
u8 j;
|
||||
|
||||
|
@ -51,7 +51,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = HDA_QUAD,
|
||||
.channels_max = 8,
|
||||
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
|
||||
@ -213,7 +213,7 @@ static int skl_be_prepare(struct snd_pcm_substream *substream,
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
struct skl_module_cfg *mconfig;
|
||||
|
||||
if ((dai->playback_active > 1) || (dai->capture_active > 1))
|
||||
if (dai->playback_widget->power || dai->capture_widget->power)
|
||||
return 0;
|
||||
|
||||
mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
|
||||
@ -402,23 +402,33 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct skl_module_cfg *mconfig;
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
|
||||
struct snd_soc_dapm_widget *w;
|
||||
int ret;
|
||||
|
||||
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
|
||||
if (!mconfig)
|
||||
return -EIO;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
w = dai->playback_widget;
|
||||
else
|
||||
w = dai->capture_widget;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
skl_pcm_prepare(substream, dai);
|
||||
/*
|
||||
* enable DMA Resume enable bit for the stream, set the dpib
|
||||
* & lpib position to resune before starting the DMA
|
||||
*/
|
||||
snd_hdac_ext_stream_drsm_enable(ebus, true,
|
||||
hdac_stream(stream)->index);
|
||||
snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
|
||||
snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
|
||||
if (!w->ignore_suspend) {
|
||||
skl_pcm_prepare(substream, dai);
|
||||
/*
|
||||
* enable DMA Resume enable bit for the stream, set the
|
||||
* dpib & lpib position to resume before starting the
|
||||
* DMA
|
||||
*/
|
||||
snd_hdac_ext_stream_drsm_enable(ebus, true,
|
||||
hdac_stream(stream)->index);
|
||||
snd_hdac_ext_stream_set_dpibr(ebus, stream,
|
||||
stream->dpib);
|
||||
snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
|
||||
}
|
||||
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
@ -448,7 +458,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
return ret;
|
||||
|
||||
ret = skl_decoupled_trigger(substream, cmd);
|
||||
if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
|
||||
if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) {
|
||||
/* save the dpib and lpib positions */
|
||||
stream->dpib = readl(ebus->bus.remap_addr +
|
||||
AZX_REG_VS_SDXDPIB_XBASE +
|
||||
@ -523,7 +533,6 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
if (!link)
|
||||
return -EINVAL;
|
||||
|
||||
snd_hdac_ext_bus_link_power_up(link);
|
||||
snd_hdac_ext_link_stream_reset(link_dev);
|
||||
|
||||
snd_hdac_ext_link_stream_setup(link_dev, format_val);
|
||||
@ -682,7 +691,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.playback = {
|
||||
.stream_name = "HDMI1 Playback",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.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 |
|
||||
@ -697,7 +706,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.playback = {
|
||||
.stream_name = "HDMI2 Playback",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.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 |
|
||||
@ -712,7 +721,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.playback = {
|
||||
.stream_name = "HDMI3 Playback",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.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 |
|
||||
@ -759,13 +768,85 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "SSP2 Pin",
|
||||
.ops = &skl_be_ssp_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "ssp2 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ssp2 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "SSP3 Pin",
|
||||
.ops = &skl_be_ssp_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "ssp3 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ssp3 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "SSP4 Pin",
|
||||
.ops = &skl_be_ssp_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "ssp4 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ssp4 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "SSP5 Pin",
|
||||
.ops = &skl_be_ssp_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "ssp5 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ssp5 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "iDisp1 Pin",
|
||||
.ops = &skl_link_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "iDisp1 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE,
|
||||
@ -777,7 +858,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.playback = {
|
||||
.stream_name = "iDisp2 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
@ -790,7 +871,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
.playback = {
|
||||
.stream_name = "iDisp3 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
|
@ -336,8 +336,6 @@ void skl_dsp_free(struct sst_dsp *dsp)
|
||||
skl_ipc_int_disable(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_int_disable(dsp);
|
||||
|
||||
|
@ -118,16 +118,25 @@ struct skl_dsp_fw_ops {
|
||||
int (*set_state_D0)(struct sst_dsp *ctx);
|
||||
int (*set_state_D3)(struct sst_dsp *ctx);
|
||||
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
|
||||
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name);
|
||||
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
|
||||
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
|
||||
|
||||
};
|
||||
|
||||
struct skl_dsp_loader_ops {
|
||||
int stream_tag;
|
||||
|
||||
int (*alloc_dma_buf)(struct device *dev,
|
||||
struct snd_dma_buffer *dmab, size_t size);
|
||||
int (*free_dma_buf)(struct device *dev,
|
||||
struct snd_dma_buffer *dmab);
|
||||
int (*prepare)(struct device *dev, unsigned int format,
|
||||
unsigned int byte_size,
|
||||
struct snd_dma_buffer *bufp);
|
||||
int (*trigger)(struct device *dev, bool start, int stream_tag);
|
||||
|
||||
int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab,
|
||||
int stream_tag);
|
||||
};
|
||||
|
||||
struct skl_load_module_info {
|
||||
@ -160,6 +169,10 @@ int skl_dsp_boot(struct sst_dsp *ctx);
|
||||
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
|
||||
struct skl_sst **dsp);
|
||||
int 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);
|
||||
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
|
||||
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
|
||||
|
||||
#endif /*__SKL_SST_DSP_H__*/
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/uuid.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../common/sst-ipc.h"
|
||||
@ -304,14 +305,16 @@ static int skl_transfer_module(struct sst_dsp *ctx,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid)
|
||||
static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
|
||||
{
|
||||
struct skl_module_table *module_entry = NULL;
|
||||
int ret = 0;
|
||||
char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
|
||||
uuid_le *uuid_mod;
|
||||
|
||||
snprintf(mod_name, sizeof(mod_name), "%s%s%s",
|
||||
"intel/dsp_fw_", guid, ".bin");
|
||||
uuid_mod = (uuid_le *)guid;
|
||||
snprintf(mod_name, sizeof(mod_name), "%s%pUL%s",
|
||||
"intel/dsp_fw_", uuid_mod, ".bin");
|
||||
|
||||
module_entry = skl_module_get_from_id(ctx, mod_id);
|
||||
if (module_entry == NULL) {
|
||||
@ -451,6 +454,10 @@ void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
|
||||
skl_clear_module_table(ctx->dsp);
|
||||
skl_ipc_free(&ctx->ipc);
|
||||
ctx->dsp->ops->free(ctx->dsp);
|
||||
if (ctx->boot_complete) {
|
||||
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
|
||||
skl_cldma_int_disable(ctx->dsp);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
|
||||
|
||||
|
@ -154,13 +154,32 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
|
||||
}
|
||||
|
||||
static void skl_tplg_update_chmap(struct skl_module_fmt *fmt, int chs)
|
||||
{
|
||||
int slot_map = 0xFFFFFFFF;
|
||||
int start_slot = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chs; i++) {
|
||||
/*
|
||||
* For 2 channels with starting slot as 0, slot map will
|
||||
* look like 0xFFFFFF10.
|
||||
*/
|
||||
slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i)));
|
||||
start_slot++;
|
||||
}
|
||||
fmt->ch_map = slot_map;
|
||||
}
|
||||
|
||||
static void skl_tplg_update_params(struct skl_module_fmt *fmt,
|
||||
struct skl_pipe_params *params, int fixup)
|
||||
{
|
||||
if (fixup & SKL_RATE_FIXUP_MASK)
|
||||
fmt->s_freq = params->s_freq;
|
||||
if (fixup & SKL_CH_FIXUP_MASK)
|
||||
if (fixup & SKL_CH_FIXUP_MASK) {
|
||||
fmt->channels = params->ch;
|
||||
skl_tplg_update_chmap(fmt, fmt->channels);
|
||||
}
|
||||
if (fixup & SKL_FMT_FIXUP_MASK) {
|
||||
fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
|
||||
|
||||
@ -1564,6 +1583,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
||||
return -ENOMEM;
|
||||
|
||||
w->priv = mconfig;
|
||||
memcpy(&mconfig->guid, &dfw_config->uuid, 16);
|
||||
|
||||
mconfig->id.module_id = dfw_config->module_id;
|
||||
mconfig->id.instance_id = dfw_config->instance_id;
|
||||
mconfig->mcps = dfw_config->max_mcps;
|
||||
@ -1593,10 +1614,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
||||
mconfig->time_slot = dfw_config->time_slot;
|
||||
mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
|
||||
|
||||
if (dfw_config->is_loadable)
|
||||
memcpy(mconfig->guid, dfw_config->uuid,
|
||||
ARRAY_SIZE(dfw_config->uuid));
|
||||
|
||||
mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
|
||||
sizeof(*mconfig->m_in_pin),
|
||||
GFP_KERNEL);
|
||||
|
@ -281,7 +281,7 @@ enum skl_module_state {
|
||||
};
|
||||
|
||||
struct skl_module_cfg {
|
||||
char guid[SKL_UUID_STR_SZ];
|
||||
u8 guid[16];
|
||||
struct skl_module_inst_id id;
|
||||
u8 domain;
|
||||
bool homogenous_inputs;
|
||||
|
@ -181,7 +181,7 @@ struct skl_dfw_pipe {
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_module {
|
||||
char uuid[SKL_UUID_STR_SZ];
|
||||
u8 uuid[16];
|
||||
|
||||
u16 module_id;
|
||||
u16 instance_id;
|
||||
|
@ -229,7 +229,12 @@ static int skl_suspend(struct device *dev)
|
||||
* running, we need to save the state for these and continue
|
||||
*/
|
||||
if (skl->supend_active) {
|
||||
/* turn off the links and stop the CORB/RIRB DMA if it is On */
|
||||
snd_hdac_ext_bus_link_power_down_all(ebus);
|
||||
|
||||
if (ebus->cmd_dma_state)
|
||||
snd_hdac_bus_stop_cmd_io(&ebus->bus);
|
||||
|
||||
enable_irq_wake(bus->irq);
|
||||
pci_save_state(pci);
|
||||
pci_disable_device(pci);
|
||||
@ -255,6 +260,7 @@ static int skl_resume(struct device *dev)
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int ret;
|
||||
|
||||
/* Turned OFF in HDMI codec driver after codec reconfiguration */
|
||||
@ -276,8 +282,29 @@ static int skl_resume(struct device *dev)
|
||||
ret = pci_enable_device(pci);
|
||||
snd_hdac_ext_bus_link_power_up_all(ebus);
|
||||
disable_irq_wake(bus->irq);
|
||||
/*
|
||||
* turn On the links which are On before active suspend
|
||||
* and start the CORB/RIRB DMA if On before
|
||||
* active suspend.
|
||||
*/
|
||||
list_for_each_entry(hlink, &ebus->hlink_list, list) {
|
||||
if (hlink->ref_count)
|
||||
snd_hdac_ext_bus_link_power_up(hlink);
|
||||
}
|
||||
|
||||
if (ebus->cmd_dma_state)
|
||||
snd_hdac_bus_init_cmd_io(&ebus->bus);
|
||||
} else {
|
||||
ret = _skl_resume(ebus);
|
||||
|
||||
/* turn off the links which are off before suspend */
|
||||
list_for_each_entry(hlink, &ebus->hlink_list, list) {
|
||||
if (!hlink->ref_count)
|
||||
snd_hdac_ext_bus_link_power_down(hlink);
|
||||
}
|
||||
|
||||
if (!ebus->cmd_dma_state)
|
||||
snd_hdac_bus_stop_cmd_io(&ebus->bus);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -613,6 +640,7 @@ static int skl_probe(struct pci_dev *pci,
|
||||
struct skl *skl;
|
||||
struct hdac_ext_bus *ebus = NULL;
|
||||
struct hdac_bus *bus = NULL;
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int err;
|
||||
|
||||
/* we use ext core ops, so provide NULL for ops here */
|
||||
@ -643,7 +671,7 @@ static int skl_probe(struct pci_dev *pci,
|
||||
err = skl_machine_device_register(skl,
|
||||
(void *)pci_id->driver_data);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
goto out_nhlt_free;
|
||||
|
||||
err = skl_init_dsp(skl);
|
||||
if (err < 0) {
|
||||
@ -679,6 +707,12 @@ static int skl_probe(struct pci_dev *pci,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* we are done probling so decrement link counts
|
||||
*/
|
||||
list_for_each_entry(hlink, &ebus->hlink_list, list)
|
||||
snd_hdac_ext_bus_link_put(ebus, hlink);
|
||||
|
||||
/*configure PM */
|
||||
pm_runtime_put_noidle(bus->dev);
|
||||
pm_runtime_allow(bus->dev);
|
||||
@ -693,6 +727,8 @@ out_dsp_free:
|
||||
skl_free_dsp(skl);
|
||||
out_mach_free:
|
||||
skl_machine_device_unregister(skl);
|
||||
out_nhlt_free:
|
||||
skl_nhlt_free(skl->nhlt);
|
||||
out_free:
|
||||
skl->init_failed = 1;
|
||||
skl_free(ebus);
|
||||
@ -743,6 +779,7 @@ static void skl_remove(struct pci_dev *pci)
|
||||
skl_free_dsp(skl);
|
||||
skl_machine_device_unregister(skl);
|
||||
skl_dmic_device_unregister(skl);
|
||||
skl_nhlt_free(skl->nhlt);
|
||||
skl_free(ebus);
|
||||
dev_set_drvdata(&pci->dev, NULL);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ struct skl {
|
||||
struct platform_device *dmic_dev;
|
||||
struct platform_device *i2s_dev;
|
||||
|
||||
void *nhlt; /* nhlt ptr */
|
||||
struct nhlt_acpi_table *nhlt; /* nhlt ptr */
|
||||
struct skl_sst *skl_sst; /* sst skl ctx */
|
||||
|
||||
struct skl_dsp_resource resource;
|
||||
@ -103,8 +103,8 @@ struct skl_dsp_ops {
|
||||
int skl_platform_unregister(struct device *dev);
|
||||
int skl_platform_register(struct device *dev);
|
||||
|
||||
void *skl_nhlt_init(struct device *dev);
|
||||
void skl_nhlt_free(void *addr);
|
||||
struct nhlt_acpi_table *skl_nhlt_init(struct device *dev);
|
||||
void skl_nhlt_free(struct nhlt_acpi_table *addr);
|
||||
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
|
||||
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
|
||||
|
||||
|
@ -34,6 +34,13 @@ struct rk_i2s_dev {
|
||||
|
||||
struct regmap *regmap;
|
||||
|
||||
/*
|
||||
* Used to indicate the tx/rx status.
|
||||
* I2S controller hopes to start the tx and rx together,
|
||||
* also to stop them when they are both try to stop.
|
||||
*/
|
||||
bool tx_start;
|
||||
bool rx_start;
|
||||
bool is_master_mode;
|
||||
};
|
||||
|
||||
@ -75,29 +82,37 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
|
||||
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START,
|
||||
I2S_XFER_TXS_START);
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
|
||||
|
||||
i2s->tx_start = true;
|
||||
} else {
|
||||
i2s->tx_start = false;
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
||||
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START,
|
||||
I2S_XFER_TXS_STOP);
|
||||
if (!i2s->rx_start) {
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START |
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_STOP |
|
||||
I2S_XFER_RXS_STOP);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_TXC,
|
||||
I2S_CLR_TXC);
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC);
|
||||
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val & I2S_CLR_TXC) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,29 +128,37 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
|
||||
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_RXS_START);
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
|
||||
|
||||
i2s->rx_start = true;
|
||||
} else {
|
||||
i2s->rx_start = false;
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
||||
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_RXS_STOP);
|
||||
if (!i2s->tx_start) {
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START |
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_STOP |
|
||||
I2S_XFER_RXS_STOP);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_RXC,
|
||||
I2S_CLR_RXC);
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC);
|
||||
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val & I2S_CLR_RXC) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -930,7 +930,18 @@ static struct snd_soc_component *soc_find_component(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai *snd_soc_find_dai(
|
||||
/**
|
||||
* snd_soc_find_dai - Find a registered DAI
|
||||
*
|
||||
* @dlc: name of the DAI and optional component info to match
|
||||
*
|
||||
* This function will search all regsitered components and their DAIs to
|
||||
* find the DAI of the same name. The component's of_node and name
|
||||
* should also match if being specified.
|
||||
*
|
||||
* Return: pointer of DAI, or NULL if not found.
|
||||
*/
|
||||
struct snd_soc_dai *snd_soc_find_dai(
|
||||
const struct snd_soc_dai_link_component *dlc)
|
||||
{
|
||||
struct snd_soc_component *component;
|
||||
@ -959,6 +970,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_find_dai);
|
||||
|
||||
static bool soc_is_dai_link_bound(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
|
@ -163,31 +163,42 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare formats mask for valid/allowed sample types. If the dma does
|
||||
* not have support for the given physical word size, it needs to be
|
||||
* masked out so user space can not use the format which produces
|
||||
* corrupted audio.
|
||||
* In case the dma driver does not implement the slave_caps the default
|
||||
* assumption is that it supports 1, 2 and 4 bytes widths.
|
||||
* If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
|
||||
* hw.formats set to 0, meaning no restrictions are in place.
|
||||
* In this case it's the responsibility of the DAI driver to
|
||||
* provide the supported format information.
|
||||
*/
|
||||
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
int bits = snd_pcm_format_physical_width(i);
|
||||
if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
|
||||
/*
|
||||
* Prepare formats mask for valid/allowed sample types. If the
|
||||
* dma does not have support for the given physical word size,
|
||||
* it needs to be masked out so user space can not use the
|
||||
* format which produces corrupted audio.
|
||||
* In case the dma driver does not implement the slave_caps the
|
||||
* default assumption is that it supports 1, 2 and 4 bytes
|
||||
* widths.
|
||||
*/
|
||||
for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
int bits = snd_pcm_format_physical_width(i);
|
||||
|
||||
/* Enable only samples with DMA supported physical widths */
|
||||
switch (bits) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
case 64:
|
||||
if (addr_widths & (1 << (bits / 8)))
|
||||
hw.formats |= (1LL << i);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported types */
|
||||
break;
|
||||
/*
|
||||
* Enable only samples with DMA supported physical
|
||||
* widths
|
||||
*/
|
||||
switch (bits) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
case 64:
|
||||
if (addr_widths & (1 << (bits / 8)))
|
||||
hw.formats |= (1LL << i);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported types */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return snd_soc_set_runtime_hwparams(substream, &hw);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user