mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-04 04:02:26 +00:00
sound updates for 4.7-rc1
This time was again a relatively calm development cycle; most of updates are about drivers, and no radical changes are seen in any core code. Here are some highlights: ALSA core: - Continued hardening of ALSA hrtimer - A few leak fixes in timer interface - Fix poll error handling in PCM and compress - Add error propagation in compress API - Removal of dead rtctimer driver HD-audio: - Native ELD notify support for i915 HDMI - Realtek ALC234 & co support - Code refactoring to standardize chmap support - Continued development for SKL HDMI core support Firewire: - Apply delayed card registration to all drivers - Improved / stabilized the handling of PCM stream start / stop - Add tracepoints to dump a part of isochronous packet data - Fixed incoming/outgoing packet parameter usages - Add support for M-Audio profire series USB-audio: - Fixes for UAC2 clock source - SS+ support - Workaround for oft-seen repeated sample rate read errors ASoC: - Further slow progress on the topology code - Substantial updates and improvements for the da7219, es8328, fsl-ssi, Intel and rcar drivers. - Compress error handling in WM ADSP driver -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXPYgvAAoJEGwxgFQ9KSmka3IQAJfXxKYyL0mqOgUpFav2QprE j4nQFSQf2KMAHgod1iF4Pv5glRZ3T8CbWllu/+GT87ny4wwJH76D07VCZSnrA+cv NMxRMN8QiGWS+eNPDNqRbcpzQvgwRK17VAmvpIfZtdntq3IryPLyCnY+FJ6Xt5v7 CjgGjlKJQ8i6AJVtoKVlrCOTBPS8YezQ7o67v8+BNrHDyOr0pwLERhvqJBRjaCbj fKj+JNDsWyu4kX0nInKNGah+5Qiib68+UNK5M+/PnoWv9tEOBPNXeWqRkcRpwnrF t1BQLnKGdlcSIufXcvxHDdxLftJZ38w+EbnQ/2r+SYHYIwPqTWdvVeXZUiq70wW/ WBUEOHybaHTNc52nMpjo/PU72CHa29zvKq+QHMXMRmFfVrLepIgEpBRBUjENtCjM 3OUn1IhYiNI4FOfgLm5duuYSBVdS4C2qstBDMtGpP64l7AmBZMFtbGUP8pKhvpzF FR2VoQpBFLPo805lQBKYbxdpzUGqfR7M/O73WRMzB/ZPZa95VNCDoRDQBbYF4Wzy SByVcE56znxoS9AmbhU6LzCXxdyVp6YAXZNR0pHp+8QdrRoFQZwRhfNVN3FIeNub COV+0pCQ2GTYvVdfLjdh6VT4shXeg5ZrUVnE3akL+8OzXow9lKyhknvLHn71aTZi HT0vSirSdrEYf4zg6wtB =QsAc -----END PGP SIGNATURE----- Merge tag 'sound-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "This time was again a relatively calm development cycle; most of updates are about drivers, and no radical changes are seen in any core code. Here are some highlights: ALSA core: - Continued hardening of ALSA hrtimer - A few leak fixes in timer interface - Fix poll error handling in PCM and compress - Add error propagation in compress API - Removal of dead rtctimer driver HD-audio: - Native ELD notify support for i915 HDMI - Realtek ALC234 & co support - Code refactoring to standardize chmap support - Continued development for SKL HDMI core support Firewire: - Apply delayed card registration to all drivers - Improved / stabilized the handling of PCM stream start / stop - Add tracepoints to dump a part of isochronous packet data - Fixed incoming/outgoing packet parameter usages - Add support for M-Audio profire series USB-audio: - Fixes for UAC2 clock source - SS+ support - Workaround for oft-seen repeated sample rate read errors ASoC: - Further slow progress on the topology code - Substantial updates and improvements for the da7219, es8328, fsl-ssi, Intel and rcar drivers. - Compress error handling in WM ADSP driver" * tag 'sound-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (177 commits) ALSA: firewire-lib: change a member of event structure to suppress sparse wanings to bool type sound: oss: Use setup_timer and mod_timer. ASoC: hdac_hdmi: Remove the unused 'timeout' variable ASoC: fsl_ssi: Fix channel slipping on capture (or playback) restart in full duplex. ASoC: fsl_ssi: Fix channel slipping in Playback at startup ASoC: fsl_ssi: Fix samples being dropped at Playback startup ASoC: fsl_ssi: Save a dev reference for dev_err() purpose. ASoC: fsl_ssi: The IPG/5 limitation concerns the bitclk, not the sysclk. ASoC: fsl_ssi: Real hardware channels max number is 32 ASoC: pcm5102a: Add support for PCM5102A codec ASoC: hdac_hdmi: add link management ASoC: Intel: Skylake: add link management ALSA: hdac: add link pm and ref counting ALSA: au88x0: Fix zero clear of stream->resources ASoC: rt298: Add DMI match for Broxton-P reference platform ASoC: rt298: fix null deref on acpi driver data ASoC: dapm: deprecate MICBIAS widget type ALSA: firewire-lib: drop skip argument from helper functions to queue a packet ALSA: firewire-lib: add context information to tracepoints ALSA: firewire-lib: permit to flush queued packets only in process context for better PCM period granularity ...
This commit is contained in:
commit
f4c80d5a16
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";
|
||||
};
|
@ -655,17 +655,6 @@ development branches in general while the development for the current
|
||||
and next kernels are found in for-linus and for-next branches,
|
||||
respectively.
|
||||
|
||||
If you are using the latest Linus tree, it'd be better to pull the
|
||||
above GIT tree onto it. If you are using the older kernels, an easy
|
||||
way to try the latest ALSA code is to build from the snapshot
|
||||
tarball. There are daily tarballs and the latest snapshot tarball.
|
||||
All can be built just like normal alsa-driver release packages, that
|
||||
is, installed via the usual spells: configure, make and make
|
||||
install(-modules). See INSTALL in the package. The snapshot tarballs
|
||||
are found at:
|
||||
|
||||
- ftp://ftp.suse.com/pub/people/tiwai/snapshot/
|
||||
|
||||
|
||||
Sending a Bug Report
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@ -699,7 +688,12 @@ problems.
|
||||
alsa-info
|
||||
~~~~~~~~~
|
||||
The script `alsa-info.sh` is a very useful tool to gather the audio
|
||||
device information. You can fetch the latest version from:
|
||||
device information. It's included in alsa-utils package. The latest
|
||||
version can be found on git repository:
|
||||
|
||||
- git://git.alsa-project.org/alsa-utils.git
|
||||
|
||||
The script can be fetched directly from the following URL, too:
|
||||
|
||||
- http://www.alsa-project.org/alsa-info.sh
|
||||
|
||||
@ -836,15 +830,11 @@ can get a proc-file dump at the current state, get a list of control
|
||||
(mixer) elements, set/get the control element value, simulate the PCM
|
||||
operation, the jack plugging simulation, etc.
|
||||
|
||||
The package is found in:
|
||||
|
||||
- ftp://ftp.suse.com/pub/people/tiwai/misc/
|
||||
|
||||
A git repository is available:
|
||||
The program is found in the git repository below:
|
||||
|
||||
- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
|
||||
|
||||
See README file in the tarball for more details about hda-emu
|
||||
See README file in the repository for more details about hda-emu
|
||||
program.
|
||||
|
||||
|
||||
|
@ -149,7 +149,7 @@ Gapless Playback
|
||||
================
|
||||
When playing thru an album, the decoders have the ability to skip the encoder
|
||||
delay and padding and directly move from one track content to another. The end
|
||||
user can perceive this as gapless playback as we dont have silence while
|
||||
user can perceive this as gapless playback as we don't have silence while
|
||||
switching from one track to another
|
||||
|
||||
Also, there might be low-intensity noises due to encoding. Perfect gapless is
|
||||
@ -184,7 +184,7 @@ Sequence flow for gapless would be:
|
||||
- Fill data of the first track
|
||||
- Trigger start
|
||||
- User-space finished sending all,
|
||||
- Indicaite next track data by sending set_next_track
|
||||
- Indicate next track data by sending set_next_track
|
||||
- Set metadata of the next track
|
||||
- then call partial_drain to flush most of buffer in DSP
|
||||
- Fill data of the next track
|
||||
|
@ -132,7 +132,7 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
|
||||
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
|
||||
ARRAY_SIZE(wm8731_output_mixer_controls)),
|
||||
|
||||
If you dont want the mixer elements prefixed with the name of the mixer widget,
|
||||
If you don't want the mixer elements prefixed with the name of the mixer widget,
|
||||
you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
|
||||
as for SND_SOC_DAPM_MIXER.
|
||||
|
||||
|
@ -63,7 +63,7 @@ multiple re-usable component drivers :-
|
||||
and any audio DSP drivers for that platform.
|
||||
|
||||
* Machine class driver: The machine driver class acts as the glue that
|
||||
decribes and binds the other component drivers together to form an ALSA
|
||||
describes and binds the other component drivers together to form an ALSA
|
||||
"sound card device". It handles any machine specific controls and
|
||||
machine level audio events (e.g. turning on an amp at start of playback).
|
||||
|
||||
|
@ -129,7 +129,7 @@ will be required to issue multiple queries and perform an
|
||||
interpolation of the results
|
||||
|
||||
In some hardware-specific configuration, the system timestamp is
|
||||
latched by a low-level audio subsytem, and the information provided
|
||||
latched by a low-level audio subsystem, and the information provided
|
||||
back to the driver. Due to potential delays in the communication with
|
||||
the hardware, there is a risk of misalignment with the avail and delay
|
||||
information. To make sure applications are not confused, a
|
||||
|
@ -4732,6 +4732,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
|
||||
|
@ -448,5 +448,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(
|
||||
|
@ -36,6 +36,8 @@ struct hdac_chmap_ops {
|
||||
int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
|
||||
int channels, unsigned char *chmap);
|
||||
|
||||
int (*get_spk_alloc)(struct hdac_device *hdac, int pcm_idx);
|
||||
|
||||
void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
|
||||
unsigned char *chmap);
|
||||
void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
|
||||
|
@ -10,8 +10,8 @@
|
||||
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable);
|
||||
int snd_hdac_display_power(struct hdac_bus *bus, bool enable);
|
||||
void snd_hdac_i915_set_bclk(struct hdac_bus *bus);
|
||||
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate);
|
||||
int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
|
||||
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate);
|
||||
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
|
||||
bool *audio_enabled, char *buffer, int max_bytes);
|
||||
int snd_hdac_i915_init(struct hdac_bus *bus);
|
||||
int snd_hdac_i915_exit(struct hdac_bus *bus);
|
||||
@ -28,12 +28,12 @@ static inline int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
|
||||
static inline void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
|
||||
{
|
||||
}
|
||||
static inline int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid,
|
||||
int rate)
|
||||
static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec,
|
||||
hda_nid_t nid, int rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
|
||||
static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
|
||||
bool *audio_enabled, char *buffer,
|
||||
int max_bytes)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -672,7 +672,7 @@ enum {
|
||||
|
||||
/* global timers (device member) */
|
||||
#define SNDRV_TIMER_GLOBAL_SYSTEM 0
|
||||
#define SNDRV_TIMER_GLOBAL_RTC 1
|
||||
#define SNDRV_TIMER_GLOBAL_RTC 1 /* unused */
|
||||
#define SNDRV_TIMER_GLOBAL_HPET 2
|
||||
#define SNDRV_TIMER_GLOBAL_HRTIMER 3
|
||||
|
||||
|
@ -141,35 +141,6 @@ config SND_SEQ_HRTIMER_DEFAULT
|
||||
Say Y here to use the HR-timer backend as the default sequencer
|
||||
timer.
|
||||
|
||||
config SND_RTCTIMER
|
||||
tristate "RTC Timer support"
|
||||
depends on RTC
|
||||
select SND_TIMER
|
||||
help
|
||||
Say Y here to enable RTC timer support for ALSA. ALSA uses
|
||||
the RTC timer as a precise timing source and maps the RTC
|
||||
timer to ALSA's timer interface. The ALSA sequencer code also
|
||||
can use this timing source.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-rtctimer.
|
||||
|
||||
Note that this option is exclusive with the new RTC drivers
|
||||
(CONFIG_RTC_CLASS) since this requires the old API.
|
||||
|
||||
config SND_SEQ_RTCTIMER_DEFAULT
|
||||
bool "Use RTC as default sequencer timer"
|
||||
depends on SND_RTCTIMER && SND_SEQUENCER
|
||||
depends on !SND_SEQ_HRTIMER_DEFAULT
|
||||
default y
|
||||
help
|
||||
Say Y here to use the RTC timer as the default sequencer
|
||||
timer. This is strongly recommended because it ensures
|
||||
precise MIDI timing even when the system timer runs at less
|
||||
than 1000 Hz.
|
||||
|
||||
If in doubt, say Y.
|
||||
|
||||
config SND_DYNAMIC_MINORS
|
||||
bool "Dynamic device file minor numbers"
|
||||
help
|
||||
|
@ -37,7 +37,6 @@ obj-$(CONFIG_SND) += snd.o
|
||||
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
|
||||
obj-$(CONFIG_SND_TIMER) += snd-timer.o
|
||||
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
|
||||
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
|
||||
obj-$(CONFIG_SND_PCM) += snd-pcm.o
|
||||
obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
|
||||
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
|
||||
|
@ -288,9 +288,12 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
|
||||
stream = &data->stream;
|
||||
mutex_lock(&stream->device->lock);
|
||||
/* write is allowed when stream is running or has been steup */
|
||||
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
|
||||
stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
|
||||
stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
|
||||
switch (stream->runtime->state) {
|
||||
case SNDRV_PCM_STATE_SETUP:
|
||||
case SNDRV_PCM_STATE_PREPARED:
|
||||
case SNDRV_PCM_STATE_RUNNING:
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&stream->device->lock);
|
||||
return -EBADFD;
|
||||
}
|
||||
@ -391,14 +394,13 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
|
||||
int retval = 0;
|
||||
|
||||
if (snd_BUG_ON(!data))
|
||||
return -EFAULT;
|
||||
return POLLERR;
|
||||
|
||||
stream = &data->stream;
|
||||
if (snd_BUG_ON(!stream))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&stream->device->lock);
|
||||
if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
|
||||
retval = -EBADFD;
|
||||
retval = snd_compr_get_poll(stream) | POLLERR;
|
||||
goto out;
|
||||
}
|
||||
poll_wait(f, &stream->runtime->sleep, wait);
|
||||
@ -421,10 +423,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
|
||||
retval = snd_compr_get_poll(stream);
|
||||
break;
|
||||
default:
|
||||
if (stream->direction == SND_COMPRESS_PLAYBACK)
|
||||
retval = POLLOUT | POLLWRNORM | POLLERR;
|
||||
else
|
||||
retval = POLLIN | POLLRDNORM | POLLERR;
|
||||
retval = snd_compr_get_poll(stream) | POLLERR;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
@ -802,9 +801,9 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
|
||||
|
||||
if (snd_BUG_ON(!data))
|
||||
return -EFAULT;
|
||||
|
||||
stream = &data->stream;
|
||||
if (snd_BUG_ON(!stream))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&stream->device->lock);
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
|
||||
|
@ -38,37 +38,53 @@ static unsigned int resolution;
|
||||
struct snd_hrtimer {
|
||||
struct snd_timer *timer;
|
||||
struct hrtimer hrt;
|
||||
atomic_t running;
|
||||
bool in_callback;
|
||||
};
|
||||
|
||||
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
|
||||
{
|
||||
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
|
||||
struct snd_timer *t = stime->timer;
|
||||
unsigned long oruns;
|
||||
ktime_t delta;
|
||||
unsigned long ticks;
|
||||
enum hrtimer_restart ret = HRTIMER_NORESTART;
|
||||
|
||||
if (!atomic_read(&stime->running))
|
||||
return HRTIMER_NORESTART;
|
||||
spin_lock(&t->lock);
|
||||
if (!t->running)
|
||||
goto out; /* fast path */
|
||||
stime->in_callback = true;
|
||||
ticks = t->sticks;
|
||||
spin_unlock(&t->lock);
|
||||
|
||||
oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
|
||||
snd_timer_interrupt(stime->timer, t->sticks * oruns);
|
||||
/* calculate the drift */
|
||||
delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
|
||||
if (delta.tv64 > 0)
|
||||
ticks += ktime_divns(delta, ticks * resolution);
|
||||
|
||||
if (!atomic_read(&stime->running))
|
||||
return HRTIMER_NORESTART;
|
||||
return HRTIMER_RESTART;
|
||||
snd_timer_interrupt(stime->timer, ticks);
|
||||
|
||||
spin_lock(&t->lock);
|
||||
if (t->running) {
|
||||
hrtimer_add_expires_ns(hrt, t->sticks * resolution);
|
||||
ret = HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
stime->in_callback = false;
|
||||
out:
|
||||
spin_unlock(&t->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_hrtimer_open(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime;
|
||||
|
||||
stime = kmalloc(sizeof(*stime), GFP_KERNEL);
|
||||
stime = kzalloc(sizeof(*stime), GFP_KERNEL);
|
||||
if (!stime)
|
||||
return -ENOMEM;
|
||||
hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
stime->timer = t;
|
||||
stime->hrt.function = snd_hrtimer_callback;
|
||||
atomic_set(&stime->running, 0);
|
||||
t->private_data = stime;
|
||||
return 0;
|
||||
}
|
||||
@ -78,6 +94,11 @@ static int snd_hrtimer_close(struct snd_timer *t)
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
|
||||
if (stime) {
|
||||
spin_lock_irq(&t->lock);
|
||||
t->running = 0; /* just to be sure */
|
||||
stime->in_callback = 1; /* skip start/stop */
|
||||
spin_unlock_irq(&t->lock);
|
||||
|
||||
hrtimer_cancel(&stime->hrt);
|
||||
kfree(stime);
|
||||
t->private_data = NULL;
|
||||
@ -89,18 +110,19 @@ static int snd_hrtimer_start(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
|
||||
atomic_set(&stime->running, 0);
|
||||
hrtimer_try_to_cancel(&stime->hrt);
|
||||
if (stime->in_callback)
|
||||
return 0;
|
||||
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
|
||||
HRTIMER_MODE_REL);
|
||||
atomic_set(&stime->running, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hrtimer_stop(struct snd_timer *t)
|
||||
{
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
atomic_set(&stime->running, 0);
|
||||
|
||||
if (stime->in_callback)
|
||||
return 0;
|
||||
hrtimer_try_to_cancel(&stime->hrt);
|
||||
return 0;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -1886,8 +1886,8 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
|
||||
snd_timer_interrupt(substream->timer, 1);
|
||||
#endif
|
||||
_end:
|
||||
snd_pcm_stream_unlock_irqrestore(substream, flags);
|
||||
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
|
||||
snd_pcm_stream_unlock_irqrestore(substream, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_pcm_period_elapsed);
|
||||
@ -2595,6 +2595,8 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (WARN_ON(pcm->streams[stream].chmap_kctl))
|
||||
return -EBUSY;
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
@ -3161,7 +3161,7 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
|
||||
|
||||
substream = pcm_file->substream;
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
return -ENXIO;
|
||||
return POLLOUT | POLLWRNORM | POLLERR;
|
||||
runtime = substream->runtime;
|
||||
|
||||
poll_wait(file, &runtime->sleep, wait);
|
||||
@ -3200,7 +3200,7 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
|
||||
|
||||
substream = pcm_file->substream;
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
return -ENXIO;
|
||||
return POLLIN | POLLRDNORM | POLLERR;
|
||||
runtime = substream->runtime;
|
||||
|
||||
poll_wait(file, &runtime->sleep, wait);
|
||||
|
@ -1,187 +0,0 @@
|
||||
/*
|
||||
* RTC based high-frequency timer
|
||||
*
|
||||
* Copyright (C) 2000 Takashi Iwai
|
||||
* based on rtctimer.c by Steve Ratcliffe
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/log2.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/timer.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_RTC)
|
||||
|
||||
#include <linux/mc146818rtc.h>
|
||||
|
||||
#define RTC_FREQ 1024 /* default frequency */
|
||||
#define NANO_SEC 1000000000L /* 10^9 in sec */
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int rtctimer_open(struct snd_timer *t);
|
||||
static int rtctimer_close(struct snd_timer *t);
|
||||
static int rtctimer_start(struct snd_timer *t);
|
||||
static int rtctimer_stop(struct snd_timer *t);
|
||||
|
||||
|
||||
/*
|
||||
* The hardware dependent description for this timer.
|
||||
*/
|
||||
static struct snd_timer_hardware rtc_hw = {
|
||||
.flags = SNDRV_TIMER_HW_AUTO |
|
||||
SNDRV_TIMER_HW_FIRST |
|
||||
SNDRV_TIMER_HW_TASKLET,
|
||||
.ticks = 100000000L, /* FIXME: XXX */
|
||||
.open = rtctimer_open,
|
||||
.close = rtctimer_close,
|
||||
.start = rtctimer_start,
|
||||
.stop = rtctimer_stop,
|
||||
};
|
||||
|
||||
static int rtctimer_freq = RTC_FREQ; /* frequency */
|
||||
static struct snd_timer *rtctimer;
|
||||
static struct tasklet_struct rtc_tasklet;
|
||||
static rtc_task_t rtc_task;
|
||||
|
||||
|
||||
static int
|
||||
rtctimer_open(struct snd_timer *t)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rtc_register(&rtc_task);
|
||||
if (err < 0)
|
||||
return err;
|
||||
t->private_data = &rtc_task;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtctimer_close(struct snd_timer *t)
|
||||
{
|
||||
rtc_task_t *rtc = t->private_data;
|
||||
if (rtc) {
|
||||
rtc_unregister(rtc);
|
||||
tasklet_kill(&rtc_tasklet);
|
||||
t->private_data = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtctimer_start(struct snd_timer *timer)
|
||||
{
|
||||
rtc_task_t *rtc = timer->private_data;
|
||||
if (snd_BUG_ON(!rtc))
|
||||
return -EINVAL;
|
||||
rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
|
||||
rtc_control(rtc, RTC_PIE_ON, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtctimer_stop(struct snd_timer *timer)
|
||||
{
|
||||
rtc_task_t *rtc = timer->private_data;
|
||||
if (snd_BUG_ON(!rtc))
|
||||
return -EINVAL;
|
||||
rtc_control(rtc, RTC_PIE_OFF, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtctimer_tasklet(unsigned long data)
|
||||
{
|
||||
snd_timer_interrupt((struct snd_timer *)data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt
|
||||
*/
|
||||
static void rtctimer_interrupt(void *private_data)
|
||||
{
|
||||
tasklet_schedule(private_data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ENTRY functions
|
||||
*/
|
||||
static int __init rtctimer_init(void)
|
||||
{
|
||||
int err;
|
||||
struct snd_timer *timer;
|
||||
|
||||
if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
|
||||
!is_power_of_2(rtctimer_freq)) {
|
||||
pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create a new timer and set up the fields */
|
||||
err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
timer->module = THIS_MODULE;
|
||||
strcpy(timer->name, "RTC timer");
|
||||
timer->hw = rtc_hw;
|
||||
timer->hw.resolution = NANO_SEC / rtctimer_freq;
|
||||
|
||||
tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
|
||||
|
||||
/* set up RTC callback */
|
||||
rtc_task.func = rtctimer_interrupt;
|
||||
rtc_task.private_data = &rtc_tasklet;
|
||||
|
||||
err = snd_timer_global_register(timer);
|
||||
if (err < 0) {
|
||||
snd_timer_global_free(timer);
|
||||
return err;
|
||||
}
|
||||
rtctimer = timer; /* remember this */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rtctimer_exit(void)
|
||||
{
|
||||
if (rtctimer) {
|
||||
snd_timer_global_free(rtctimer);
|
||||
rtctimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* exported stuff
|
||||
*/
|
||||
module_init(rtctimer_init)
|
||||
module_exit(rtctimer_exit)
|
||||
|
||||
module_param(rtctimer_freq, int, 0444);
|
||||
MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_RTC) */
|
@ -47,8 +47,6 @@ int seq_default_timer_card = -1;
|
||||
int seq_default_timer_device =
|
||||
#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
|
||||
SNDRV_TIMER_GLOBAL_HRTIMER
|
||||
#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
|
||||
SNDRV_TIMER_GLOBAL_RTC
|
||||
#else
|
||||
SNDRV_TIMER_GLOBAL_SYSTEM
|
||||
#endif
|
||||
|
@ -37,8 +37,6 @@
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_HRTIMER)
|
||||
#define DEFAULT_TIMER_LIMIT 4
|
||||
#elif IS_ENABLED(CONFIG_SND_RTCTIMER)
|
||||
#define DEFAULT_TIMER_LIMIT 2
|
||||
#else
|
||||
#define DEFAULT_TIMER_LIMIT 1
|
||||
#endif
|
||||
@ -1225,6 +1223,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
|
||||
tu->tstamp = *tstamp;
|
||||
if ((tu->filter & (1 << event)) == 0 || !tu->tread)
|
||||
return;
|
||||
memset(&r1, 0, sizeof(r1));
|
||||
r1.event = event;
|
||||
r1.tstamp = *tstamp;
|
||||
r1.val = resolution;
|
||||
@ -1267,6 +1266,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
|
||||
}
|
||||
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
|
||||
tu->last_resolution != resolution) {
|
||||
memset(&r1, 0, sizeof(r1));
|
||||
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
|
||||
r1.tstamp = tstamp;
|
||||
r1.val = resolution;
|
||||
@ -1739,6 +1739,7 @@ static int snd_timer_user_params(struct file *file,
|
||||
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
|
||||
if (tu->tread) {
|
||||
struct snd_timer_tread tread;
|
||||
memset(&tread, 0, sizeof(tread));
|
||||
tread.event = SNDRV_TIMER_EVENT_EARLY;
|
||||
tread.tstamp.tv_sec = 0;
|
||||
tread.tstamp.tv_nsec = 0;
|
||||
|
@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM
|
||||
Say Y here to include support for TASCAM.
|
||||
* FW-1884
|
||||
* FW-1082
|
||||
* FW-1804
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-firewire-tascam.
|
||||
|
@ -1,3 +1,6 @@
|
||||
# To find a header included by define_trace.h.
|
||||
CFLAGS_amdtp-stream.o := -I$(src)
|
||||
|
||||
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
|
||||
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
|
||||
snd-isight-objs := isight.o
|
||||
|
110
sound/firewire/amdtp-stream-trace.h
Normal file
110
sound/firewire/amdtp-stream-trace.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
|
||||
*
|
||||
* Copyright (c) 2016 Takashi Sakamoto
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM snd_firewire_lib
|
||||
|
||||
#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _AMDTP_STREAM_TRACE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(in_packet,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
__field(int, channel)
|
||||
__field(int, src)
|
||||
__field(int, dest)
|
||||
__field(u32, cip_header0)
|
||||
__field(u32, cip_header1)
|
||||
__field(unsigned int, payload_quadlets)
|
||||
__field(unsigned int, packet_index)
|
||||
__field(unsigned int, irq)
|
||||
__field(unsigned int, index)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->second = cycles / CYCLES_PER_SECOND;
|
||||
__entry->cycle = cycles % CYCLES_PER_SECOND;
|
||||
__entry->channel = s->context->channel;
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->cip_header0 = cip_header[0];
|
||||
__entry->cip_header1 = cip_header[1];
|
||||
__entry->payload_quadlets = payload_quadlets;
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
"%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
|
||||
__entry->second,
|
||||
__entry->cycle,
|
||||
__entry->src,
|
||||
__entry->dest,
|
||||
__entry->channel,
|
||||
__entry->cip_header0,
|
||||
__entry->cip_header1,
|
||||
__entry->payload_quadlets,
|
||||
__entry->packet_index,
|
||||
__entry->irq,
|
||||
__entry->index)
|
||||
);
|
||||
|
||||
TRACE_EVENT(out_packet,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
__field(int, channel)
|
||||
__field(int, src)
|
||||
__field(int, dest)
|
||||
__field(u32, cip_header0)
|
||||
__field(u32, cip_header1)
|
||||
__field(unsigned int, payload_quadlets)
|
||||
__field(unsigned int, packet_index)
|
||||
__field(unsigned int, irq)
|
||||
__field(unsigned int, index)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->second = cycles / CYCLES_PER_SECOND;
|
||||
__entry->cycle = cycles % CYCLES_PER_SECOND;
|
||||
__entry->channel = s->context->channel;
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->node_id;
|
||||
__entry->cip_header0 = be32_to_cpu(cip_header[0]);
|
||||
__entry->cip_header1 = be32_to_cpu(cip_header[1]);
|
||||
__entry->payload_quadlets = payload_length / 4;
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
"%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
|
||||
__entry->second,
|
||||
__entry->cycle,
|
||||
__entry->src,
|
||||
__entry->dest,
|
||||
__entry->channel,
|
||||
__entry->cip_header0,
|
||||
__entry->cip_header1,
|
||||
__entry->payload_quadlets,
|
||||
__entry->packet_index,
|
||||
__entry->irq,
|
||||
__entry->index)
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE amdtp-stream-trace
|
||||
#include <trace/define_trace.h>
|
@ -19,6 +19,10 @@
|
||||
#define CYCLES_PER_SECOND 8000
|
||||
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
|
||||
|
||||
/* Always support Linux tracing subsystem. */
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "amdtp-stream-trace.h"
|
||||
|
||||
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
|
||||
|
||||
/* isochronous header parameters */
|
||||
@ -87,7 +91,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
|
||||
init_waitqueue_head(&s->callback_wait);
|
||||
s->callbacked = false;
|
||||
s->sync_slave = NULL;
|
||||
|
||||
s->fmt = fmt;
|
||||
s->process_data_blocks = process_data_blocks;
|
||||
@ -102,6 +105,10 @@ EXPORT_SYMBOL(amdtp_stream_init);
|
||||
*/
|
||||
void amdtp_stream_destroy(struct amdtp_stream *s)
|
||||
{
|
||||
/* Not initialized. */
|
||||
if (s->protocol == NULL)
|
||||
return;
|
||||
|
||||
WARN_ON(amdtp_stream_running(s));
|
||||
kfree(s->protocol);
|
||||
mutex_destroy(&s->mutex);
|
||||
@ -244,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
|
||||
tasklet_kill(&s->period_tasklet);
|
||||
s->pcm_buffer_pointer = 0;
|
||||
s->pcm_period_pointer = 0;
|
||||
s->pointer_flush = true;
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
|
||||
|
||||
@ -349,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s,
|
||||
s->pcm_period_pointer += frames;
|
||||
if (s->pcm_period_pointer >= pcm->runtime->period_size) {
|
||||
s->pcm_period_pointer -= pcm->runtime->period_size;
|
||||
s->pointer_flush = false;
|
||||
tasklet_hi_schedule(&s->period_tasklet);
|
||||
}
|
||||
}
|
||||
@ -363,9 +368,8 @@ static void pcm_period_tasklet(unsigned long data)
|
||||
snd_pcm_period_elapsed(pcm);
|
||||
}
|
||||
|
||||
static int queue_packet(struct amdtp_stream *s,
|
||||
unsigned int header_length,
|
||||
unsigned int payload_length, bool skip)
|
||||
static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
|
||||
unsigned int payload_length)
|
||||
{
|
||||
struct fw_iso_packet p = {0};
|
||||
int err = 0;
|
||||
@ -376,8 +380,10 @@ static int queue_packet(struct amdtp_stream *s,
|
||||
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
|
||||
p.tag = TAG_CIP;
|
||||
p.header_length = header_length;
|
||||
p.payload_length = (!skip) ? payload_length : 0;
|
||||
p.skip = skip;
|
||||
if (payload_length > 0)
|
||||
p.payload_length = payload_length;
|
||||
else
|
||||
p.skip = true;
|
||||
err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
|
||||
s->buffer.packets[s->packet_index].offset);
|
||||
if (err < 0) {
|
||||
@ -392,27 +398,30 @@ static int queue_packet(struct amdtp_stream *s,
|
||||
}
|
||||
|
||||
static inline int queue_out_packet(struct amdtp_stream *s,
|
||||
unsigned int payload_length, bool skip)
|
||||
unsigned int payload_length)
|
||||
{
|
||||
return queue_packet(s, OUT_PACKET_HEADER_SIZE,
|
||||
payload_length, skip);
|
||||
return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
|
||||
}
|
||||
|
||||
static inline int queue_in_packet(struct amdtp_stream *s)
|
||||
{
|
||||
return queue_packet(s, IN_PACKET_HEADER_SIZE,
|
||||
amdtp_stream_get_max_payload(s), false);
|
||||
amdtp_stream_get_max_payload(s));
|
||||
}
|
||||
|
||||
static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
|
||||
unsigned int syt)
|
||||
static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
|
||||
unsigned int index)
|
||||
{
|
||||
__be32 *buffer;
|
||||
unsigned int syt;
|
||||
unsigned int data_blocks;
|
||||
unsigned int payload_length;
|
||||
unsigned int pcm_frames;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
syt = calculate_syt(s, cycle);
|
||||
data_blocks = calculate_data_blocks(s, syt);
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
|
||||
|
||||
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
|
||||
@ -424,9 +433,11 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
|
||||
(syt & CIP_SYT_MASK));
|
||||
|
||||
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
|
||||
|
||||
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
|
||||
if (queue_out_packet(s, payload_length, false) < 0)
|
||||
|
||||
trace_out_packet(s, cycle, buffer, payload_length, index);
|
||||
|
||||
if (queue_out_packet(s, payload_length) < 0)
|
||||
return -EIO;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
@ -438,19 +449,24 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
|
||||
}
|
||||
|
||||
static int handle_in_packet(struct amdtp_stream *s,
|
||||
unsigned int payload_quadlets, __be32 *buffer,
|
||||
unsigned int *data_blocks, unsigned int syt)
|
||||
unsigned int payload_quadlets, unsigned int cycle,
|
||||
unsigned int index)
|
||||
{
|
||||
__be32 *buffer;
|
||||
u32 cip_header[2];
|
||||
unsigned int fmt, fdf;
|
||||
unsigned int fmt, fdf, syt;
|
||||
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
|
||||
unsigned int data_blocks;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
bool lost;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
cip_header[0] = be32_to_cpu(buffer[0]);
|
||||
cip_header[1] = be32_to_cpu(buffer[1]);
|
||||
|
||||
trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
|
||||
|
||||
/*
|
||||
* This module supports 'Two-quadlet CIP header with SYT field'.
|
||||
* For convenience, also check FMT field is AM824 or not.
|
||||
@ -460,7 +476,7 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
dev_info_ratelimited(&s->unit->device,
|
||||
"Invalid CIP header for AMDTP: %08X:%08X\n",
|
||||
cip_header[0], cip_header[1]);
|
||||
*data_blocks = 0;
|
||||
data_blocks = 0;
|
||||
pcm_frames = 0;
|
||||
goto end;
|
||||
}
|
||||
@ -471,7 +487,7 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
dev_info_ratelimited(&s->unit->device,
|
||||
"Detect unexpected protocol: %08x %08x\n",
|
||||
cip_header[0], cip_header[1]);
|
||||
*data_blocks = 0;
|
||||
data_blocks = 0;
|
||||
pcm_frames = 0;
|
||||
goto end;
|
||||
}
|
||||
@ -480,7 +496,7 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
|
||||
if (payload_quadlets < 3 ||
|
||||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
|
||||
*data_blocks = 0;
|
||||
data_blocks = 0;
|
||||
} else {
|
||||
data_block_quadlets =
|
||||
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
|
||||
@ -494,12 +510,12 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
if (s->flags & CIP_WRONG_DBS)
|
||||
data_block_quadlets = s->data_block_quadlets;
|
||||
|
||||
*data_blocks = (payload_quadlets - 2) / data_block_quadlets;
|
||||
data_blocks = (payload_quadlets - 2) / data_block_quadlets;
|
||||
}
|
||||
|
||||
/* Check data block counter continuity */
|
||||
data_block_counter = cip_header[0] & CIP_DBC_MASK;
|
||||
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
|
||||
if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
|
||||
s->data_block_counter != UINT_MAX)
|
||||
data_block_counter = s->data_block_counter;
|
||||
|
||||
@ -510,10 +526,10 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
lost = data_block_counter != s->data_block_counter;
|
||||
} else {
|
||||
if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
|
||||
if (data_blocks > 0 && s->tx_dbc_interval > 0)
|
||||
dbc_interval = s->tx_dbc_interval;
|
||||
else
|
||||
dbc_interval = *data_blocks;
|
||||
dbc_interval = data_blocks;
|
||||
|
||||
lost = data_block_counter !=
|
||||
((s->data_block_counter + dbc_interval) & 0xff);
|
||||
@ -526,13 +542,14 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
|
||||
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
s->data_block_counter = data_block_counter;
|
||||
else
|
||||
s->data_block_counter =
|
||||
(data_block_counter + *data_blocks) & 0xff;
|
||||
(data_block_counter + data_blocks) & 0xff;
|
||||
end:
|
||||
if (queue_in_packet(s) < 0)
|
||||
return -EIO;
|
||||
@ -544,29 +561,50 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
|
||||
/*
|
||||
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
|
||||
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
|
||||
* it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
|
||||
*/
|
||||
static inline u32 compute_cycle_count(u32 tstamp)
|
||||
{
|
||||
return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
|
||||
}
|
||||
|
||||
static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
|
||||
{
|
||||
cycle += addend;
|
||||
if (cycle >= 8 * CYCLES_PER_SECOND)
|
||||
cycle -= 8 * CYCLES_PER_SECOND;
|
||||
return cycle;
|
||||
}
|
||||
|
||||
static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
|
||||
{
|
||||
if (cycle < subtrahend)
|
||||
cycle += 8 * CYCLES_PER_SECOND;
|
||||
return cycle - subtrahend;
|
||||
}
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
unsigned int i, syt, packets = header_length / 4;
|
||||
unsigned int data_blocks;
|
||||
unsigned int i, packets = header_length / 4;
|
||||
u32 cycle;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Compute the cycle of the last queued packet.
|
||||
* (We need only the four lowest bits for the SYT, so we can ignore
|
||||
* that bits 0-11 must wrap around at 3072.)
|
||||
*/
|
||||
cycle += QUEUE_LENGTH - packets;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
/* Align to actual cycle count for the last packet. */
|
||||
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
syt = calculate_syt(s, ++cycle);
|
||||
data_blocks = calculate_data_blocks(s, syt);
|
||||
|
||||
if (handle_out_packet(s, data_blocks, syt) < 0) {
|
||||
cycle = increment_cycle_count(cycle, 1);
|
||||
if (handle_out_packet(s, cycle, i) < 0) {
|
||||
s->packet_index = -1;
|
||||
amdtp_stream_pcm_abort(s);
|
||||
return;
|
||||
@ -576,15 +614,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
|
||||
fw_iso_context_queue_flush(s->context);
|
||||
}
|
||||
|
||||
static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
|
||||
static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
unsigned int p, syt, packets;
|
||||
unsigned int i, packets;
|
||||
unsigned int payload_quadlets, max_payload_quadlets;
|
||||
unsigned int data_blocks;
|
||||
__be32 *buffer, *headers = header;
|
||||
__be32 *headers = header;
|
||||
u32 cycle;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
@ -592,70 +630,44 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
|
||||
/* The number of packets in buffer */
|
||||
packets = header_length / IN_PACKET_HEADER_SIZE;
|
||||
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
/* Align to actual cycle count for the last packet. */
|
||||
cycle = decrement_cycle_count(cycle, packets);
|
||||
|
||||
/* For buffer-over-run prevention. */
|
||||
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
|
||||
|
||||
for (p = 0; p < packets; p++) {
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
for (i = 0; i < packets; i++) {
|
||||
cycle = increment_cycle_count(cycle, 1);
|
||||
|
||||
/* The number of quadlets in this packet */
|
||||
payload_quadlets =
|
||||
(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
|
||||
(be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
|
||||
if (payload_quadlets > max_payload_quadlets) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect jumbo payload: %02x %02x\n",
|
||||
payload_quadlets, max_payload_quadlets);
|
||||
s->packet_index = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
|
||||
if (handle_in_packet(s, payload_quadlets, buffer,
|
||||
&data_blocks, syt) < 0) {
|
||||
s->packet_index = -1;
|
||||
if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process sync slave stream */
|
||||
if (s->sync_slave && s->sync_slave->callbacked) {
|
||||
if (handle_out_packet(s->sync_slave,
|
||||
data_blocks, syt) < 0) {
|
||||
s->packet_index = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Queueing error or detecting discontinuity */
|
||||
if (s->packet_index < 0) {
|
||||
/* Queueing error or detecting invalid payload. */
|
||||
if (i < packets) {
|
||||
s->packet_index = -1;
|
||||
amdtp_stream_pcm_abort(s);
|
||||
|
||||
/* Abort sync slave. */
|
||||
if (s->sync_slave) {
|
||||
s->sync_slave->packet_index = -1;
|
||||
amdtp_stream_pcm_abort(s->sync_slave);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* when sync to device, flush the packets for slave stream */
|
||||
if (s->sync_slave && s->sync_slave->callbacked)
|
||||
fw_iso_context_queue_flush(s->sync_slave->context);
|
||||
|
||||
fw_iso_context_queue_flush(s->context);
|
||||
}
|
||||
|
||||
/* processing is done by master callback */
|
||||
static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* this is executed one time */
|
||||
static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
||||
u32 cycle, size_t header_length,
|
||||
u32 tstamp, size_t header_length,
|
||||
void *header, void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
@ -669,12 +681,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
context->callback.sc = in_stream_callback;
|
||||
else if (s->flags & CIP_SYNC_TO_DEVICE)
|
||||
context->callback.sc = slave_stream_callback;
|
||||
else
|
||||
context->callback.sc = out_stream_callback;
|
||||
|
||||
context->callback.sc(context, cycle, header_length, header, s);
|
||||
context->callback.sc(context, tstamp, header_length, header, s);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -713,8 +723,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM &&
|
||||
s->flags & CIP_SKIP_INIT_DBC_CHECK)
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
s->data_block_counter = UINT_MAX;
|
||||
else
|
||||
s->data_block_counter = 0;
|
||||
@ -755,7 +764,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
err = queue_in_packet(s);
|
||||
else
|
||||
err = queue_out_packet(s, 0, true);
|
||||
err = queue_out_packet(s, 0);
|
||||
if (err < 0)
|
||||
goto err_context;
|
||||
} while (s->packet_index > 0);
|
||||
@ -794,11 +803,24 @@ EXPORT_SYMBOL(amdtp_stream_start);
|
||||
*/
|
||||
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
|
||||
{
|
||||
/* this optimization is allowed to be racy */
|
||||
if (s->pointer_flush && amdtp_stream_running(s))
|
||||
/*
|
||||
* This function is called in software IRQ context of period_tasklet or
|
||||
* process context.
|
||||
*
|
||||
* When the software IRQ context was scheduled by software IRQ context
|
||||
* of IR/IT contexts, queued packets were already handled. Therefore,
|
||||
* no need to flush the queue in buffer anymore.
|
||||
*
|
||||
* When the process context reach here, some packets will be already
|
||||
* queued in the buffer. These packets should be handled immediately
|
||||
* to keep better granularity of PCM pointer.
|
||||
*
|
||||
* Later, the process context will sometimes schedules software IRQ
|
||||
* context of the period_tasklet. Then, no need to flush the queue by
|
||||
* the same reason as described for IR/IT contexts.
|
||||
*/
|
||||
if (!in_interrupt() && amdtp_stream_running(s))
|
||||
fw_iso_context_flush_completions(s->context);
|
||||
else
|
||||
s->pointer_flush = true;
|
||||
|
||||
return ACCESS_ONCE(s->pcm_buffer_pointer);
|
||||
}
|
||||
|
@ -17,8 +17,6 @@
|
||||
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or
|
||||
* SYT_INTERVAL samples, with these two types alternating so that
|
||||
* the overall sample rate comes out right.
|
||||
* @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
|
||||
* generated by in packets. Defaultly this driver generates timestamp.
|
||||
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
|
||||
* @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
|
||||
* corresponds to the end of event in the packet. Out of IEC 61883.
|
||||
@ -26,8 +24,6 @@
|
||||
* The value of data_block_quadlets is used instead of reported value.
|
||||
* @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
|
||||
* skipped for detecting discontinuity.
|
||||
* @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
|
||||
* packet is not continuous from an initial value.
|
||||
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
|
||||
* packet is wrong but the others are correct.
|
||||
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
|
||||
@ -37,14 +33,12 @@
|
||||
enum cip_flags {
|
||||
CIP_NONBLOCKING = 0x00,
|
||||
CIP_BLOCKING = 0x01,
|
||||
CIP_SYNC_TO_DEVICE = 0x02,
|
||||
CIP_EMPTY_WITH_TAG0 = 0x04,
|
||||
CIP_DBC_IS_END_EVENT = 0x08,
|
||||
CIP_WRONG_DBS = 0x10,
|
||||
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
|
||||
CIP_SKIP_INIT_DBC_CHECK = 0x40,
|
||||
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
|
||||
CIP_JUMBO_PAYLOAD = 0x100,
|
||||
CIP_EMPTY_WITH_TAG0 = 0x02,
|
||||
CIP_DBC_IS_END_EVENT = 0x04,
|
||||
CIP_WRONG_DBS = 0x08,
|
||||
CIP_SKIP_DBC_ZERO_CHECK = 0x10,
|
||||
CIP_EMPTY_HAS_WRONG_DBC = 0x20,
|
||||
CIP_JUMBO_PAYLOAD = 0x40,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -132,12 +126,10 @@ struct amdtp_stream {
|
||||
struct tasklet_struct period_tasklet;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
bool pointer_flush;
|
||||
|
||||
/* To wait for first packet. */
|
||||
bool callbacked;
|
||||
wait_queue_head_t callback_wait;
|
||||
struct amdtp_stream *sync_slave;
|
||||
|
||||
/* For backends to process data blocks. */
|
||||
void *protocol;
|
||||
@ -223,23 +215,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
|
||||
return sfc & 1;
|
||||
}
|
||||
|
||||
static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
|
||||
struct amdtp_stream *master,
|
||||
struct amdtp_stream *slave)
|
||||
{
|
||||
if (sync_mode == CIP_SYNC_TO_DEVICE) {
|
||||
master->flags |= CIP_SYNC_TO_DEVICE;
|
||||
slave->flags |= CIP_SYNC_TO_DEVICE;
|
||||
master->sync_slave = slave;
|
||||
} else {
|
||||
master->flags &= ~CIP_SYNC_TO_DEVICE;
|
||||
slave->flags &= ~CIP_SYNC_TO_DEVICE;
|
||||
master->sync_slave = NULL;
|
||||
}
|
||||
|
||||
slave->sync_slave = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_wait_callback - sleep till callbacked or timeout
|
||||
* @s: the AMDTP stream
|
||||
|
@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
|
||||
#define MODEL_MAUDIO_PROJECTMIX 0x00010091
|
||||
|
||||
static int
|
||||
name_device(struct snd_bebob *bebob, unsigned int vendor_id)
|
||||
name_device(struct snd_bebob *bebob)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(bebob->unit);
|
||||
char vendor[24] = {0};
|
||||
@ -126,6 +126,17 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void bebob_free(struct snd_bebob *bebob)
|
||||
{
|
||||
snd_bebob_stream_destroy_duplex(bebob);
|
||||
fw_unit_put(bebob->unit);
|
||||
|
||||
kfree(bebob->maudio_special_quirk);
|
||||
|
||||
mutex_destroy(&bebob->mutex);
|
||||
kfree(bebob);
|
||||
}
|
||||
|
||||
/*
|
||||
* This module releases the FireWire unit data after all ALSA character devices
|
||||
* are released by applications. This is for releasing stream data or finishing
|
||||
@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_bebob *bebob = card->private_data;
|
||||
|
||||
snd_bebob_stream_destroy_duplex(bebob);
|
||||
fw_unit_put(bebob->unit);
|
||||
mutex_lock(&devices_mutex);
|
||||
clear_bit(bebob->card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
|
||||
kfree(bebob->maudio_special_quirk);
|
||||
|
||||
if (bebob->card_index >= 0) {
|
||||
mutex_lock(&devices_mutex);
|
||||
clear_bit(bebob->card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
}
|
||||
|
||||
mutex_destroy(&bebob->mutex);
|
||||
bebob_free(card->private_data);
|
||||
}
|
||||
|
||||
static const struct snd_bebob_spec *
|
||||
@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit)
|
||||
return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bebob_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
static void
|
||||
do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_bebob *bebob;
|
||||
const struct snd_bebob_spec *spec;
|
||||
struct snd_bebob *bebob =
|
||||
container_of(work, struct snd_bebob, dwork.work);
|
||||
unsigned int card_index;
|
||||
int err;
|
||||
|
||||
if (bebob->registered)
|
||||
return;
|
||||
|
||||
mutex_lock(&devices_mutex);
|
||||
|
||||
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
|
||||
@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit,
|
||||
break;
|
||||
}
|
||||
if (card_index >= SNDRV_CARDS) {
|
||||
err = -ENOENT;
|
||||
goto end;
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((entry->vendor_id == VEN_FOCUSRITE) &&
|
||||
(entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
|
||||
spec = get_saffire_spec(unit);
|
||||
else if ((entry->vendor_id == VEN_MAUDIO1) &&
|
||||
(entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
|
||||
!check_audiophile_booted(unit))
|
||||
spec = NULL;
|
||||
else
|
||||
spec = (const struct snd_bebob_spec *)entry->driver_data;
|
||||
|
||||
if (spec == NULL) {
|
||||
if ((entry->vendor_id == VEN_MAUDIO1) ||
|
||||
(entry->vendor_id == VEN_MAUDIO2))
|
||||
err = snd_bebob_maudio_load_firmware(unit);
|
||||
else
|
||||
err = -ENOSYS;
|
||||
goto end;
|
||||
err = snd_card_new(&bebob->unit->device, index[card_index],
|
||||
id[card_index], THIS_MODULE, 0, &bebob->card);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_card_new(&unit->device, index[card_index], id[card_index],
|
||||
THIS_MODULE, sizeof(struct snd_bebob), &card);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
bebob = card->private_data;
|
||||
bebob->card_index = card_index;
|
||||
set_bit(card_index, devices_used);
|
||||
card->private_free = bebob_card_free;
|
||||
|
||||
bebob->card = card;
|
||||
bebob->unit = fw_unit_get(unit);
|
||||
bebob->spec = spec;
|
||||
mutex_init(&bebob->mutex);
|
||||
spin_lock_init(&bebob->lock);
|
||||
init_waitqueue_head(&bebob->hwdep_wait);
|
||||
|
||||
err = name_device(bebob, entry->vendor_id);
|
||||
err = name_device(bebob);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if ((entry->vendor_id == VEN_MAUDIO1) &&
|
||||
(entry->model_id == MODEL_MAUDIO_FW1814))
|
||||
err = snd_bebob_maudio_special_discover(bebob, true);
|
||||
else if ((entry->vendor_id == VEN_MAUDIO1) &&
|
||||
(entry->model_id == MODEL_MAUDIO_PROJECTMIX))
|
||||
err = snd_bebob_maudio_special_discover(bebob, false);
|
||||
else
|
||||
if (bebob->spec == &maudio_special_spec) {
|
||||
if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
|
||||
err = snd_bebob_maudio_special_discover(bebob, true);
|
||||
else
|
||||
err = snd_bebob_maudio_special_discover(bebob, false);
|
||||
} else {
|
||||
err = snd_bebob_stream_discover(bebob);
|
||||
}
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_bebob_stream_init_duplex(bebob);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_bebob_proc_init(bebob);
|
||||
|
||||
if ((bebob->midi_input_ports > 0) ||
|
||||
(bebob->midi_output_ports > 0)) {
|
||||
if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
|
||||
err = snd_bebob_create_midi_devices(bebob);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_bebob_stream_init_duplex(bebob);
|
||||
err = snd_card_register(bebob->card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!bebob->maudio_special_quirk) {
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_bebob_stream_destroy_duplex(bebob);
|
||||
goto error;
|
||||
}
|
||||
set_bit(card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
|
||||
/*
|
||||
* After registered, bebob instance can be released corresponding to
|
||||
* releasing the sound card instance.
|
||||
*/
|
||||
bebob->card->private_free = bebob_card_free;
|
||||
bebob->card->private_data = bebob;
|
||||
bebob->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
mutex_unlock(&devices_mutex);
|
||||
snd_bebob_stream_destroy_duplex(bebob);
|
||||
snd_card_free(bebob->card);
|
||||
dev_info(&bebob->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int
|
||||
bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_bebob *bebob;
|
||||
const struct snd_bebob_spec *spec;
|
||||
|
||||
if (entry->vendor_id == VEN_FOCUSRITE &&
|
||||
entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
|
||||
spec = get_saffire_spec(unit);
|
||||
else if (entry->vendor_id == VEN_MAUDIO1 &&
|
||||
entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
|
||||
!check_audiophile_booted(unit))
|
||||
spec = NULL;
|
||||
else
|
||||
spec = (const struct snd_bebob_spec *)entry->driver_data;
|
||||
|
||||
if (spec == NULL) {
|
||||
if (entry->vendor_id == VEN_MAUDIO1 ||
|
||||
entry->vendor_id == VEN_MAUDIO2)
|
||||
return snd_bebob_maudio_load_firmware(unit);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL);
|
||||
if (bebob == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
bebob->unit = fw_unit_get(unit);
|
||||
bebob->entry = entry;
|
||||
bebob->spec = spec;
|
||||
dev_set_drvdata(&unit->device, bebob);
|
||||
|
||||
mutex_init(&bebob->mutex);
|
||||
spin_lock_init(&bebob->lock);
|
||||
init_waitqueue_head(&bebob->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
|
||||
|
||||
if (entry->vendor_id != VEN_MAUDIO1 ||
|
||||
(entry->model_id != MODEL_MAUDIO_FW1814 &&
|
||||
entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
|
||||
snd_fw_schedule_registration(unit, &bebob->dwork);
|
||||
} else {
|
||||
/*
|
||||
* This is a workaround. This bus reset seems to have an effect
|
||||
@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit,
|
||||
* signals from dbus and starts I/Os. To avoid I/Os till the
|
||||
* future bus reset, registration is done in next update().
|
||||
*/
|
||||
bebob->deferred_registration = true;
|
||||
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
|
||||
false, true);
|
||||
}
|
||||
|
||||
dev_set_drvdata(&unit->device, bebob);
|
||||
end:
|
||||
mutex_unlock(&devices_mutex);
|
||||
return err;
|
||||
error:
|
||||
mutex_unlock(&devices_mutex);
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit)
|
||||
if (bebob == NULL)
|
||||
return;
|
||||
|
||||
fcp_bus_reset(bebob->unit);
|
||||
|
||||
if (bebob->deferred_registration) {
|
||||
if (snd_card_register(bebob->card) < 0) {
|
||||
snd_bebob_stream_destroy_duplex(bebob);
|
||||
snd_card_free(bebob->card);
|
||||
}
|
||||
bebob->deferred_registration = false;
|
||||
}
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!bebob->registered)
|
||||
snd_fw_schedule_registration(unit, &bebob->dwork);
|
||||
else
|
||||
fcp_bus_reset(bebob->unit);
|
||||
}
|
||||
|
||||
static void bebob_remove(struct fw_unit *unit)
|
||||
@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit)
|
||||
if (bebob == NULL)
|
||||
return;
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(bebob->card);
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&bebob->dwork);
|
||||
|
||||
if (bebob->registered) {
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(bebob->card);
|
||||
} else {
|
||||
/* Don't forget this case. */
|
||||
bebob_free(bebob);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct snd_bebob_rate_spec normal_rate_spec = {
|
||||
|
@ -83,6 +83,10 @@ struct snd_bebob {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
const struct ieee1394_device_id *entry;
|
||||
const struct snd_bebob_spec *spec;
|
||||
|
||||
unsigned int midi_input_ports;
|
||||
@ -90,7 +94,6 @@ struct snd_bebob {
|
||||
|
||||
bool connected;
|
||||
|
||||
struct amdtp_stream *master;
|
||||
struct amdtp_stream tx_stream;
|
||||
struct amdtp_stream rx_stream;
|
||||
struct cmp_connection out_conn;
|
||||
@ -111,7 +114,6 @@ struct snd_bebob {
|
||||
|
||||
/* for M-Audio special devices */
|
||||
void *maudio_special_quirk;
|
||||
bool deferred_registration;
|
||||
|
||||
/* For BeBoB version quirk. */
|
||||
unsigned int version;
|
||||
|
@ -483,30 +483,6 @@ destroy_both_connections(struct snd_bebob *bebob)
|
||||
cmp_connection_destroy(&bebob->out_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
|
||||
{
|
||||
enum snd_bebob_clock_type src;
|
||||
int err;
|
||||
|
||||
err = snd_bebob_stream_get_clock_src(bebob, &src);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (src) {
|
||||
case SND_BEBOB_CLOCK_TYPE_INTERNAL:
|
||||
case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
|
||||
*sync_mode = CIP_SYNC_TO_DEVICE;
|
||||
break;
|
||||
default:
|
||||
case SND_BEBOB_CLOCK_TYPE_SYT:
|
||||
*sync_mode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
||||
unsigned int rate)
|
||||
@ -550,8 +526,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
||||
goto end;
|
||||
}
|
||||
|
||||
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
|
||||
|
||||
/*
|
||||
* BeBoB v3 transfers packets with these qurks:
|
||||
* - In the beginning of streaming, the value of dbc is incremented
|
||||
@ -584,8 +558,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
{
|
||||
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
||||
struct amdtp_stream *master, *slave;
|
||||
enum cip_flags sync_mode;
|
||||
unsigned int curr_rate;
|
||||
int err = 0;
|
||||
|
||||
@ -593,22 +565,11 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
if (bebob->substreams_counter == 0)
|
||||
goto end;
|
||||
|
||||
err = get_sync_mode(bebob, &sync_mode);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (sync_mode == CIP_SYNC_TO_DEVICE) {
|
||||
master = &bebob->tx_stream;
|
||||
slave = &bebob->rx_stream;
|
||||
} else {
|
||||
master = &bebob->rx_stream;
|
||||
slave = &bebob->tx_stream;
|
||||
}
|
||||
|
||||
/*
|
||||
* Considering JACK/FFADO streaming:
|
||||
* TODO: This can be removed hwdep functionality becomes popular.
|
||||
*/
|
||||
err = check_connection_used_by_others(bebob, master);
|
||||
err = check_connection_used_by_others(bebob, &bebob->rx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
@ -618,11 +579,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
* At bus reset, connections should not be broken here. So streams need
|
||||
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
|
||||
*/
|
||||
if (amdtp_streaming_error(master))
|
||||
amdtp_stream_stop(master);
|
||||
if (amdtp_streaming_error(slave))
|
||||
amdtp_stream_stop(slave);
|
||||
if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
|
||||
if (amdtp_streaming_error(&bebob->rx_stream))
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
if (amdtp_streaming_error(&bebob->tx_stream))
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
if (!amdtp_stream_running(&bebob->rx_stream) &&
|
||||
!amdtp_stream_running(&bebob->tx_stream))
|
||||
break_both_connections(bebob);
|
||||
|
||||
/* stop streams if rate is different */
|
||||
@ -635,16 +597,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
amdtp_stream_stop(master);
|
||||
amdtp_stream_stop(slave);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
break_both_connections(bebob);
|
||||
}
|
||||
|
||||
/* master should be always running */
|
||||
if (!amdtp_stream_running(master)) {
|
||||
amdtp_stream_set_sync(sync_mode, master, slave);
|
||||
bebob->master = master;
|
||||
|
||||
if (!amdtp_stream_running(&bebob->rx_stream)) {
|
||||
/*
|
||||
* NOTE:
|
||||
* If establishing connections at first, Yamaha GO46
|
||||
@ -666,7 +625,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = start_stream(bebob, master, rate);
|
||||
err = start_stream(bebob, &bebob->rx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP master stream:%d\n", err);
|
||||
@ -685,15 +644,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to ensure sampling rate: %d\n",
|
||||
err);
|
||||
amdtp_stream_stop(master);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait first callback */
|
||||
if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(master);
|
||||
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
err = -ETIMEDOUT;
|
||||
goto end;
|
||||
@ -701,20 +661,21 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
}
|
||||
|
||||
/* start slave if needed */
|
||||
if (!amdtp_stream_running(slave)) {
|
||||
err = start_stream(bebob, slave, rate);
|
||||
if (!amdtp_stream_running(&bebob->tx_stream)) {
|
||||
err = start_stream(bebob, &bebob->tx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP slave stream:%d\n", err);
|
||||
amdtp_stream_stop(master);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* wait first callback */
|
||||
if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(slave);
|
||||
amdtp_stream_stop(master);
|
||||
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
err = -ETIMEDOUT;
|
||||
}
|
||||
@ -725,22 +686,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
|
||||
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
struct amdtp_stream *master, *slave;
|
||||
|
||||
if (bebob->master == &bebob->rx_stream) {
|
||||
slave = &bebob->tx_stream;
|
||||
master = &bebob->rx_stream;
|
||||
} else {
|
||||
slave = &bebob->rx_stream;
|
||||
master = &bebob->tx_stream;
|
||||
}
|
||||
|
||||
if (bebob->substreams_counter == 0) {
|
||||
amdtp_stream_pcm_abort(master);
|
||||
amdtp_stream_stop(master);
|
||||
amdtp_stream_pcm_abort(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
|
||||
amdtp_stream_pcm_abort(slave);
|
||||
amdtp_stream_stop(slave);
|
||||
amdtp_stream_pcm_abort(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
|
||||
break_both_connections(bebob);
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2");
|
||||
#define WEISS_CATEGORY_ID 0x00
|
||||
#define LOUD_CATEGORY_ID 0x10
|
||||
|
||||
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
|
||||
|
||||
/*
|
||||
* Some models support several isochronous channels, while these streams are not
|
||||
* always available. In this case, add the model name to this list.
|
||||
@ -201,6 +199,10 @@ static void do_registration(struct work_struct *work)
|
||||
|
||||
dice_card_strings(dice);
|
||||
|
||||
err = snd_dice_stream_init_duplex(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_dice_create_proc(dice);
|
||||
|
||||
err = snd_dice_create_pcm(dice);
|
||||
@ -229,28 +231,14 @@ static void do_registration(struct work_struct *work)
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_dice_stream_destroy_duplex(dice);
|
||||
snd_dice_transaction_destroy(dice);
|
||||
snd_dice_stream_destroy_duplex(dice);
|
||||
snd_card_free(dice->card);
|
||||
dev_info(&dice->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static void schedule_registration(struct snd_dice *dice)
|
||||
{
|
||||
struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
|
||||
u64 now, delay;
|
||||
|
||||
now = get_jiffies_64();
|
||||
delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
|
||||
|
||||
if (time_after64(delay, now))
|
||||
delay -= now;
|
||||
else
|
||||
delay = 0;
|
||||
|
||||
mod_delayed_work(system_wq, &dice->dwork, delay);
|
||||
}
|
||||
|
||||
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
||||
{
|
||||
struct snd_dice *dice;
|
||||
@ -273,15 +261,9 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
|
||||
init_completion(&dice->clock_accepted);
|
||||
init_waitqueue_head(&dice->hwdep_wait);
|
||||
|
||||
err = snd_dice_stream_init_duplex(dice);
|
||||
if (err < 0) {
|
||||
dice_free(dice);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
|
||||
schedule_registration(dice);
|
||||
snd_fw_schedule_registration(unit, &dice->dwork);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -312,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit)
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!dice->registered)
|
||||
schedule_registration(dice);
|
||||
snd_fw_schedule_registration(unit, &dice->dwork);
|
||||
|
||||
/* The handler address register becomes initialized. */
|
||||
snd_dice_transaction_reinit(dice);
|
||||
@ -335,6 +317,13 @@ static const struct ieee1394_device_id dice_id_table[] = {
|
||||
.match_flags = IEEE1394_MATCH_VERSION,
|
||||
.version = DICE_INTERFACE,
|
||||
},
|
||||
/* M-Audio Profire 610/2626 has a different value in version field. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID,
|
||||
.vendor_id = 0x000d6c,
|
||||
.specifier_id = 0x000d6c,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
|
||||
|
@ -421,7 +421,7 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
|
||||
/* Use different mode between incoming/outgoing. */
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
|
||||
flags = CIP_NONBLOCKING;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
} else {
|
||||
flags = CIP_BLOCKING;
|
||||
|
@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
|
||||
return err;
|
||||
error:
|
||||
fw_core_remove_address_handler(&dg00x->async_handler);
|
||||
dg00x->async_handler.address_callback = NULL;
|
||||
dg00x->async_handler.callback_data = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
|
||||
{
|
||||
if (dg00x->async_handler.callback_data == NULL)
|
||||
return;
|
||||
|
||||
snd_fw_async_midi_port_destroy(&dg00x->out_control);
|
||||
fw_core_remove_address_handler(&dg00x->async_handler);
|
||||
|
||||
dg00x->async_handler.callback_data = NULL;
|
||||
}
|
||||
|
@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dg00x_card_free(struct snd_card *card)
|
||||
static void dg00x_free(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct snd_dg00x *dg00x = card->private_data;
|
||||
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
snd_dg00x_transaction_unregister(dg00x);
|
||||
|
||||
@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card)
|
||||
mutex_destroy(&dg00x->mutex);
|
||||
}
|
||||
|
||||
static int snd_dg00x_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
static void dg00x_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_dg00x *dg00x;
|
||||
dg00x_free(card->private_data);
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_dg00x *dg00x =
|
||||
container_of(work, struct snd_dg00x, dwork.work);
|
||||
int err;
|
||||
|
||||
/* create card */
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(struct snd_dg00x), &card);
|
||||
if (dg00x->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&dg00x->card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
card->private_free = dg00x_card_free;
|
||||
|
||||
/* initialize myself */
|
||||
dg00x = card->private_data;
|
||||
dg00x->card = card;
|
||||
dg00x->unit = fw_unit_get(unit);
|
||||
|
||||
mutex_init(&dg00x->mutex);
|
||||
spin_lock_init(&dg00x->lock);
|
||||
init_waitqueue_head(&dg00x->hwdep_wait);
|
||||
return;
|
||||
|
||||
err = name_card(dg00x);
|
||||
if (err < 0)
|
||||
@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(card);
|
||||
err = snd_card_register(dg00x->card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
dg00x->card->private_free = dg00x_card_free;
|
||||
dg00x->card->private_data = dg00x;
|
||||
dg00x->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_dg00x_transaction_unregister(dg00x);
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
snd_card_free(dg00x->card);
|
||||
dev_info(&dg00x->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int snd_dg00x_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_dg00x *dg00x;
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
|
||||
if (dg00x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dg00x->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dg00x);
|
||||
|
||||
return err;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
mutex_init(&dg00x->mutex);
|
||||
spin_lock_init(&dg00x->lock);
|
||||
init_waitqueue_head(&dg00x->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &dg00x->dwork);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_dg00x_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!dg00x->registered)
|
||||
snd_fw_schedule_registration(unit, &dg00x->dwork);
|
||||
|
||||
snd_dg00x_transaction_reregister(dg00x);
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
snd_dg00x_stream_update_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (dg00x->registered) {
|
||||
mutex_lock(&dg00x->mutex);
|
||||
snd_dg00x_stream_update_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_dg00x_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(dg00x->card);
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&dg00x->dwork);
|
||||
|
||||
if (dg00x->registered) {
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(dg00x->card);
|
||||
} else {
|
||||
/* Don't forget this case. */
|
||||
dg00x_free(dg00x);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id snd_dg00x_id_table[] = {
|
||||
|
@ -37,6 +37,9 @@ struct snd_dg00x {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
struct amdtp_stream tx_stream;
|
||||
struct fw_iso_resources tx_resources;
|
||||
|
||||
|
@ -168,11 +168,34 @@ get_hardware_info(struct snd_efw *efw)
|
||||
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
|
||||
memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
|
||||
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
|
||||
|
||||
/* AudioFire8 (since 2009) and AudioFirePre8 */
|
||||
if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
|
||||
efw->is_af9 = true;
|
||||
/* These models uses the same firmware. */
|
||||
if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
|
||||
hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
|
||||
hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
|
||||
hwinfo->type == MODEL_GIBSON_RIP ||
|
||||
hwinfo->type == MODEL_GIBSON_GOLDTOP)
|
||||
efw->is_fireworks3 = true;
|
||||
end:
|
||||
kfree(hwinfo);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void efw_free(struct snd_efw *efw)
|
||||
{
|
||||
snd_efw_stream_destroy_duplex(efw);
|
||||
snd_efw_transaction_remove_instance(efw);
|
||||
fw_unit_put(efw->unit);
|
||||
|
||||
kfree(efw->resp_buf);
|
||||
|
||||
mutex_destroy(&efw->mutex);
|
||||
kfree(efw);
|
||||
}
|
||||
|
||||
/*
|
||||
* This module releases the FireWire unit data after all ALSA character devices
|
||||
* are released by applications. This is for releasing stream data or finishing
|
||||
@ -184,28 +207,24 @@ efw_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_efw *efw = card->private_data;
|
||||
|
||||
snd_efw_stream_destroy_duplex(efw);
|
||||
snd_efw_transaction_remove_instance(efw);
|
||||
fw_unit_put(efw->unit);
|
||||
|
||||
kfree(efw->resp_buf);
|
||||
|
||||
if (efw->card_index >= 0) {
|
||||
mutex_lock(&devices_mutex);
|
||||
clear_bit(efw->card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
}
|
||||
|
||||
mutex_destroy(&efw->mutex);
|
||||
efw_free(card->private_data);
|
||||
}
|
||||
|
||||
static int
|
||||
efw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
static void
|
||||
do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_efw *efw;
|
||||
int card_index, err;
|
||||
struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
|
||||
unsigned int card_index;
|
||||
int err;
|
||||
|
||||
if (efw->registered)
|
||||
return;
|
||||
|
||||
mutex_lock(&devices_mutex);
|
||||
|
||||
@ -215,24 +234,16 @@ efw_probe(struct fw_unit *unit,
|
||||
break;
|
||||
}
|
||||
if (card_index >= SNDRV_CARDS) {
|
||||
err = -ENOENT;
|
||||
goto end;
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_card_new(&unit->device, index[card_index], id[card_index],
|
||||
THIS_MODULE, sizeof(struct snd_efw), &card);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
efw = card->private_data;
|
||||
efw->card_index = card_index;
|
||||
set_bit(card_index, devices_used);
|
||||
card->private_free = efw_card_free;
|
||||
|
||||
efw->card = card;
|
||||
efw->unit = fw_unit_get(unit);
|
||||
mutex_init(&efw->mutex);
|
||||
spin_lock_init(&efw->lock);
|
||||
init_waitqueue_head(&efw->hwdep_wait);
|
||||
err = snd_card_new(&efw->unit->device, index[card_index],
|
||||
id[card_index], THIS_MODULE, 0, &efw->card);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prepare response buffer */
|
||||
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
|
||||
@ -248,16 +259,10 @@ efw_probe(struct fw_unit *unit,
|
||||
err = get_hardware_info(efw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
/* AudioFire8 (since 2009) and AudioFirePre8 */
|
||||
if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
|
||||
efw->is_af9 = true;
|
||||
/* These models uses the same firmware. */
|
||||
if (entry->model_id == MODEL_ECHO_AUDIOFIRE_2 ||
|
||||
entry->model_id == MODEL_ECHO_AUDIOFIRE_4 ||
|
||||
entry->model_id == MODEL_ECHO_AUDIOFIRE_9 ||
|
||||
entry->model_id == MODEL_GIBSON_RIP ||
|
||||
entry->model_id == MODEL_GIBSON_GOLDTOP)
|
||||
efw->is_fireworks3 = true;
|
||||
|
||||
err = snd_efw_stream_init_duplex(efw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_efw_proc_init(efw);
|
||||
|
||||
@ -275,44 +280,93 @@ efw_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_efw_stream_init_duplex(efw);
|
||||
err = snd_card_register(efw->card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_efw_stream_destroy_duplex(efw);
|
||||
goto error;
|
||||
}
|
||||
set_bit(card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
|
||||
dev_set_drvdata(&unit->device, efw);
|
||||
end:
|
||||
mutex_unlock(&devices_mutex);
|
||||
return err;
|
||||
/*
|
||||
* After registered, efw instance can be released corresponding to
|
||||
* releasing the sound card instance.
|
||||
*/
|
||||
efw->card->private_free = efw_card_free;
|
||||
efw->card->private_data = efw;
|
||||
efw->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_efw_transaction_remove_instance(efw);
|
||||
mutex_unlock(&devices_mutex);
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
snd_efw_transaction_remove_instance(efw);
|
||||
snd_efw_stream_destroy_duplex(efw);
|
||||
snd_card_free(efw->card);
|
||||
dev_info(&efw->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int
|
||||
efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_efw *efw;
|
||||
|
||||
efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL);
|
||||
if (efw == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
efw->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, efw);
|
||||
|
||||
mutex_init(&efw->mutex);
|
||||
spin_lock_init(&efw->lock);
|
||||
init_waitqueue_head(&efw->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &efw->dwork);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void efw_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_efw *efw = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!efw->registered)
|
||||
snd_fw_schedule_registration(unit, &efw->dwork);
|
||||
|
||||
snd_efw_transaction_bus_reset(efw->unit);
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
snd_efw_stream_update_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (efw->registered) {
|
||||
mutex_lock(&efw->mutex);
|
||||
snd_efw_stream_update_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void efw_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_efw *efw = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(efw->card);
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&efw->dwork);
|
||||
|
||||
if (efw->registered) {
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(efw->card);
|
||||
} else {
|
||||
/* Don't forget this case. */
|
||||
efw_free(efw);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id efw_id_table[] = {
|
||||
|
@ -65,6 +65,9 @@ struct snd_efw {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
/* for transaction */
|
||||
u32 seqnum;
|
||||
bool resp_addr_changable;
|
||||
@ -81,7 +84,6 @@ struct snd_efw {
|
||||
unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
|
||||
unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
|
||||
|
||||
struct amdtp_stream *master;
|
||||
struct amdtp_stream tx_stream;
|
||||
struct amdtp_stream rx_stream;
|
||||
struct cmp_connection out_conn;
|
||||
|
@ -120,23 +120,6 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
cmp_connection_destroy(&efw->out_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
|
||||
{
|
||||
enum snd_efw_clock_source clock_source;
|
||||
int err;
|
||||
|
||||
err = snd_efw_command_get_clock_source(efw, &clock_source);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
|
||||
return -ENOSYS;
|
||||
|
||||
*sync_mode = CIP_SYNC_TO_DEVICE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
|
||||
{
|
||||
@ -208,9 +191,6 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
|
||||
|
||||
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
{
|
||||
struct amdtp_stream *master, *slave;
|
||||
unsigned int slave_substreams;
|
||||
enum cip_flags sync_mode;
|
||||
unsigned int curr_rate;
|
||||
int err = 0;
|
||||
|
||||
@ -218,32 +198,19 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
|
||||
goto end;
|
||||
|
||||
err = get_sync_mode(efw, &sync_mode);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (sync_mode == CIP_SYNC_TO_DEVICE) {
|
||||
master = &efw->tx_stream;
|
||||
slave = &efw->rx_stream;
|
||||
slave_substreams = efw->playback_substreams;
|
||||
} else {
|
||||
master = &efw->rx_stream;
|
||||
slave = &efw->tx_stream;
|
||||
slave_substreams = efw->capture_substreams;
|
||||
}
|
||||
|
||||
/*
|
||||
* Considering JACK/FFADO streaming:
|
||||
* TODO: This can be removed hwdep functionality becomes popular.
|
||||
*/
|
||||
err = check_connection_used_by_others(efw, master);
|
||||
err = check_connection_used_by_others(efw, &efw->rx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* packet queueing error */
|
||||
if (amdtp_streaming_error(slave))
|
||||
stop_stream(efw, slave);
|
||||
if (amdtp_streaming_error(master))
|
||||
stop_stream(efw, master);
|
||||
if (amdtp_streaming_error(&efw->tx_stream))
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
if (amdtp_streaming_error(&efw->rx_stream))
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
|
||||
/* stop streams if rate is different */
|
||||
err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
|
||||
@ -252,20 +219,17 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
stop_stream(efw, slave);
|
||||
stop_stream(efw, master);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
}
|
||||
|
||||
/* master should be always running */
|
||||
if (!amdtp_stream_running(master)) {
|
||||
amdtp_stream_set_sync(sync_mode, master, slave);
|
||||
efw->master = master;
|
||||
|
||||
if (!amdtp_stream_running(&efw->rx_stream)) {
|
||||
err = snd_efw_command_set_sampling_rate(efw, rate);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = start_stream(efw, master, rate);
|
||||
err = start_stream(efw, &efw->rx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP master stream:%d\n", err);
|
||||
@ -274,12 +238,13 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
}
|
||||
|
||||
/* start slave if needed */
|
||||
if (slave_substreams > 0 && !amdtp_stream_running(slave)) {
|
||||
err = start_stream(efw, slave, rate);
|
||||
if (efw->capture_substreams > 0 &&
|
||||
!amdtp_stream_running(&efw->tx_stream)) {
|
||||
err = start_stream(efw, &efw->tx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP slave stream:%d\n", err);
|
||||
stop_stream(efw, master);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
}
|
||||
}
|
||||
end:
|
||||
@ -288,26 +253,11 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
|
||||
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
||||
{
|
||||
struct amdtp_stream *master, *slave;
|
||||
unsigned int master_substreams, slave_substreams;
|
||||
if (efw->capture_substreams == 0) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
|
||||
if (efw->master == &efw->rx_stream) {
|
||||
slave = &efw->tx_stream;
|
||||
master = &efw->rx_stream;
|
||||
slave_substreams = efw->capture_substreams;
|
||||
master_substreams = efw->playback_substreams;
|
||||
} else {
|
||||
slave = &efw->rx_stream;
|
||||
master = &efw->tx_stream;
|
||||
slave_substreams = efw->playback_substreams;
|
||||
master_substreams = efw->capture_substreams;
|
||||
}
|
||||
|
||||
if (slave_substreams == 0) {
|
||||
stop_stream(efw, slave);
|
||||
|
||||
if (master_substreams == 0)
|
||||
stop_stream(efw, master);
|
||||
if (efw->playback_substreams == 0)
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_transaction);
|
||||
|
||||
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
|
||||
|
||||
/**
|
||||
* snd_fw_schedule_registration - schedule work for sound card registration
|
||||
* @unit: an instance for unit on IEEE 1394 bus
|
||||
* @dwork: delayed work with callback function
|
||||
*
|
||||
* This function is not designed for general purposes. When new unit is
|
||||
* connected to IEEE 1394 bus, the bus is under bus-reset state because of
|
||||
* topological change. In this state, units tend to fail both of asynchronous
|
||||
* and isochronous communication. To avoid this problem, this function is used
|
||||
* to postpone sound card registration after the state. The callers must
|
||||
* set up instance of delayed work in advance.
|
||||
*/
|
||||
void snd_fw_schedule_registration(struct fw_unit *unit,
|
||||
struct delayed_work *dwork)
|
||||
{
|
||||
u64 now, delay;
|
||||
|
||||
now = get_jiffies_64();
|
||||
delay = fw_parent_device(unit)->card->reset_jiffies
|
||||
+ msecs_to_jiffies(PROBE_DELAY_MS);
|
||||
|
||||
if (time_after64(delay, now))
|
||||
delay -= now;
|
||||
else
|
||||
delay = 0;
|
||||
|
||||
mod_delayed_work(system_wq, dwork, delay);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_schedule_registration);
|
||||
|
||||
static void async_midi_port_callback(struct fw_card *card, int rcode,
|
||||
void *data, size_t length,
|
||||
void *callback_data)
|
||||
|
@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode)
|
||||
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
|
||||
}
|
||||
|
||||
void snd_fw_schedule_registration(struct fw_unit *unit,
|
||||
struct delayed_work *dwork);
|
||||
|
||||
struct snd_fw_async_midi_port;
|
||||
typedef int (*snd_fw_async_midi_port_fill)(
|
||||
struct snd_rawmidi_substream *substream,
|
||||
|
@ -242,8 +242,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
||||
* blocks than IEC 61883-6 defines.
|
||||
*/
|
||||
if (stream == &oxfw->tx_stream) {
|
||||
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
|
||||
CIP_JUMBO_PAYLOAD;
|
||||
oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
|
||||
if (oxfw->wrong_dbs)
|
||||
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
}
|
||||
|
@ -118,15 +118,8 @@ static int name_card(struct snd_oxfw *oxfw)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This module releases the FireWire unit data after all ALSA character devices
|
||||
* are released by applications. This is for releasing stream data or finishing
|
||||
* transactions safely. Thus at returning from .remove(), this module still keep
|
||||
* references for the unit.
|
||||
*/
|
||||
static void oxfw_card_free(struct snd_card *card)
|
||||
static void oxfw_free(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct snd_oxfw *oxfw = card->private_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
|
||||
@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
|
||||
mutex_destroy(&oxfw->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* This module releases the FireWire unit data after all ALSA character devices
|
||||
* are released by applications. This is for releasing stream data or finishing
|
||||
* transactions safely. Thus at returning from .remove(), this module still keep
|
||||
* references for the unit.
|
||||
*/
|
||||
static void oxfw_card_free(struct snd_card *card)
|
||||
{
|
||||
oxfw_free(card->private_data);
|
||||
}
|
||||
|
||||
static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
@ -205,33 +209,18 @@ static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxfw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
static void do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_oxfw *oxfw;
|
||||
struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
|
||||
int err;
|
||||
|
||||
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
|
||||
return -ENODEV;
|
||||
if (oxfw->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(*oxfw), &card);
|
||||
err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&oxfw->card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
card->private_free = oxfw_card_free;
|
||||
oxfw = card->private_data;
|
||||
oxfw->card = card;
|
||||
mutex_init(&oxfw->mutex);
|
||||
oxfw->unit = fw_unit_get(unit);
|
||||
oxfw->entry = entry;
|
||||
spin_lock_init(&oxfw->lock);
|
||||
init_waitqueue_head(&oxfw->hwdep_wait);
|
||||
|
||||
err = snd_oxfw_stream_discover(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
return;
|
||||
|
||||
err = name_card(oxfw);
|
||||
if (err < 0)
|
||||
@ -241,6 +230,19 @@ static int oxfw_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_discover(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (oxfw->has_output) {
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = snd_oxfw_create_pcm(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
|
||||
err = snd_card_register(oxfw->card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (oxfw->has_output) {
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* After registered, oxfw instance can be released corresponding to
|
||||
* releasing the sound card instance.
|
||||
*/
|
||||
oxfw->card->private_free = oxfw_card_free;
|
||||
oxfw->card->private_data = oxfw;
|
||||
oxfw->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
|
||||
snd_card_free(oxfw->card);
|
||||
dev_info(&oxfw->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int oxfw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_oxfw *oxfw;
|
||||
|
||||
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
|
||||
return -ENODEV;
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
|
||||
if (oxfw == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
oxfw->entry = entry;
|
||||
oxfw->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, oxfw);
|
||||
|
||||
mutex_init(&oxfw->mutex);
|
||||
spin_lock_init(&oxfw->lock);
|
||||
init_waitqueue_head(&oxfw->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &oxfw->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void oxfw_bus_reset(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
|
||||
|
||||
if (!oxfw->registered)
|
||||
snd_fw_schedule_registration(unit, &oxfw->dwork);
|
||||
|
||||
fcp_bus_reset(oxfw->unit);
|
||||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
if (oxfw->registered) {
|
||||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
}
|
||||
}
|
||||
|
||||
static void oxfw_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(oxfw->card);
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&oxfw->dwork);
|
||||
|
||||
if (oxfw->registered) {
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(oxfw->card);
|
||||
} else {
|
||||
/* Don't forget this case. */
|
||||
oxfw_free(oxfw);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct compat_info griffin_firewave = {
|
||||
|
@ -36,10 +36,12 @@
|
||||
struct snd_oxfw {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
const struct device_info *device_info;
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
bool wrong_dbs;
|
||||
bool has_output;
|
||||
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
|
||||
|
@ -381,19 +381,17 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (curr_rate != rate ||
|
||||
amdtp_streaming_error(&tscm->tx_stream) ||
|
||||
amdtp_streaming_error(&tscm->rx_stream)) {
|
||||
amdtp_streaming_error(&tscm->rx_stream) ||
|
||||
amdtp_streaming_error(&tscm->tx_stream)) {
|
||||
finish_session(tscm);
|
||||
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
release_resources(tscm);
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->tx_stream)) {
|
||||
amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
|
||||
&tscm->tx_stream, &tscm->rx_stream);
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
err = keep_resources(tscm, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -406,20 +404,6 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&tscm->tx_stream,
|
||||
tscm->tx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
err = amdtp_stream_start(&tscm->rx_stream,
|
||||
tscm->rx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
@ -433,10 +417,24 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->tx_stream)) {
|
||||
err = amdtp_stream_start(&tscm->tx_stream,
|
||||
tscm->tx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
finish_session(tscm);
|
||||
release_resources(tscm);
|
||||
|
@ -85,10 +85,8 @@ static int identify_model(struct snd_tscm *tscm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tscm_card_free(struct snd_card *card)
|
||||
static void tscm_free(struct snd_tscm *tscm)
|
||||
{
|
||||
struct snd_tscm *tscm = card->private_data;
|
||||
|
||||
snd_tscm_transaction_unregister(tscm);
|
||||
snd_tscm_stream_destroy_duplex(tscm);
|
||||
|
||||
@ -97,44 +95,36 @@ static void tscm_card_free(struct snd_card *card)
|
||||
mutex_destroy(&tscm->mutex);
|
||||
}
|
||||
|
||||
static int snd_tscm_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
static void tscm_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_tscm *tscm;
|
||||
tscm_free(card->private_data);
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
{
|
||||
struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work);
|
||||
int err;
|
||||
|
||||
/* create card */
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(struct snd_tscm), &card);
|
||||
err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&tscm->card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
card->private_free = tscm_card_free;
|
||||
|
||||
/* initialize myself */
|
||||
tscm = card->private_data;
|
||||
tscm->card = card;
|
||||
tscm->unit = fw_unit_get(unit);
|
||||
|
||||
mutex_init(&tscm->mutex);
|
||||
spin_lock_init(&tscm->lock);
|
||||
init_waitqueue_head(&tscm->hwdep_wait);
|
||||
return;
|
||||
|
||||
err = identify_model(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_tscm_proc_init(tscm);
|
||||
err = snd_tscm_transaction_register(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_tscm_stream_init_duplex(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_tscm_create_pcm_devices(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
snd_tscm_proc_init(tscm);
|
||||
|
||||
err = snd_tscm_transaction_register(tscm);
|
||||
err = snd_tscm_create_pcm_devices(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@ -146,35 +136,91 @@ static int snd_tscm_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(card);
|
||||
err = snd_card_register(tscm->card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* After registered, tscm instance can be released corresponding to
|
||||
* releasing the sound card instance.
|
||||
*/
|
||||
tscm->card->private_free = tscm_card_free;
|
||||
tscm->card->private_data = tscm;
|
||||
tscm->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_tscm_transaction_unregister(tscm);
|
||||
snd_tscm_stream_destroy_duplex(tscm);
|
||||
snd_card_free(tscm->card);
|
||||
dev_info(&tscm->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int snd_tscm_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_tscm *tscm;
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL);
|
||||
if (tscm == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* initialize myself */
|
||||
tscm->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, tscm);
|
||||
|
||||
return err;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
mutex_init(&tscm->mutex);
|
||||
spin_lock_init(&tscm->lock);
|
||||
init_waitqueue_head(&tscm->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &tscm->dwork);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_tscm_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!tscm->registered)
|
||||
snd_fw_schedule_registration(unit, &tscm->dwork);
|
||||
|
||||
snd_tscm_transaction_reregister(tscm);
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
snd_tscm_stream_update_duplex(tscm);
|
||||
mutex_unlock(&tscm->mutex);
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (tscm->registered) {
|
||||
mutex_lock(&tscm->mutex);
|
||||
snd_tscm_stream_update_duplex(tscm);
|
||||
mutex_unlock(&tscm->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_tscm_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(tscm->card);
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&tscm->dwork);
|
||||
|
||||
if (tscm->registered) {
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(tscm->card);
|
||||
} else {
|
||||
/* Don't forget this case. */
|
||||
tscm_free(tscm);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id snd_tscm_id_table[] = {
|
||||
|
@ -51,6 +51,8 @@ struct snd_tscm {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
const struct snd_tscm_spec *spec;
|
||||
|
||||
struct fw_iso_resources tx_resources;
|
||||
|
@ -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);
|
||||
@ -144,6 +147,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
|
||||
if (!edev)
|
||||
return -ENOMEM;
|
||||
hdev = &edev->hdac;
|
||||
edev->ebus = ebus;
|
||||
|
||||
snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -80,6 +80,22 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
|
||||
|
||||
/* wait for cmd dmas till they are stopped */
|
||||
static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
|
||||
&& time_before(jiffies, timeout))
|
||||
udelay(10);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
|
||||
&& time_before(jiffies, timeout))
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
|
||||
* @bus: HD-audio core bus
|
||||
@ -90,6 +106,7 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
|
||||
/* disable ringbuffer DMAs */
|
||||
snd_hdac_chip_writeb(bus, RIRBCTL, 0);
|
||||
snd_hdac_chip_writeb(bus, CORBCTL, 0);
|
||||
hdac_wait_for_cmd_dmas(bus);
|
||||
/* disable unsolicited responses */
|
||||
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
|
@ -158,22 +158,40 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
|
||||
|
||||
/* There is a fixed mapping between audio pin node and display port
|
||||
* on current Intel platforms:
|
||||
/* There is a fixed mapping between audio pin node and display port.
|
||||
* on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
|
||||
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
|
||||
*
|
||||
* on VLV, ILK:
|
||||
* Pin Widget 4 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 5 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 6 - PORT D (port = 3 in i915 driver)
|
||||
*/
|
||||
static int pin2port(hda_nid_t pin_nid)
|
||||
static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
if (WARN_ON(pin_nid < 5 || pin_nid > 7))
|
||||
int base_nid;
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x80860054: /* ILK */
|
||||
case 0x80862804: /* ILK */
|
||||
case 0x80862882: /* VLV */
|
||||
base_nid = 3;
|
||||
break;
|
||||
default:
|
||||
base_nid = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
|
||||
return -1;
|
||||
return pin_nid - 4;
|
||||
return pin_nid - base_nid;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
|
||||
* @bus: HDA core bus
|
||||
* @codec: HDA codec
|
||||
* @nid: the pin widget NID
|
||||
* @rate: the sample rate to set
|
||||
*
|
||||
@ -183,14 +201,15 @@ static int pin2port(hda_nid_t pin_nid)
|
||||
* This function sets N/CTS value based on the given sample rate.
|
||||
* Returns zero for success, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
|
||||
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate)
|
||||
{
|
||||
struct hdac_bus *bus = codec->bus;
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
int port;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
|
||||
return -ENODEV;
|
||||
port = pin2port(nid);
|
||||
port = pin2port(codec, nid);
|
||||
if (port < 0)
|
||||
return -EINVAL;
|
||||
return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
|
||||
@ -199,7 +218,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component
|
||||
* @bus: HDA core bus
|
||||
* @codec: HDA codec
|
||||
* @nid: the pin widget NID
|
||||
* @audio_enabled: the pointer to store the current audio state
|
||||
* @buffer: the buffer pointer to store ELD bytes
|
||||
@ -217,16 +236,17 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
|
||||
* thus it may be over @max_bytes. If it's over @max_bytes, it implies
|
||||
* that only a part of ELD bytes have been fetched.
|
||||
*/
|
||||
int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
|
||||
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
|
||||
bool *audio_enabled, char *buffer, int max_bytes)
|
||||
{
|
||||
struct hdac_bus *bus = codec->bus;
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
int port;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
|
||||
return -ENODEV;
|
||||
|
||||
port = pin2port(nid);
|
||||
port = pin2port(codec, nid);
|
||||
if (port < 0)
|
||||
return -EINVAL;
|
||||
return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
|
||||
@ -338,6 +358,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
|
||||
struct i915_audio_component *acomp;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(hdac_acomp))
|
||||
return -EBUSY;
|
||||
|
||||
if (!i915_gfx_present())
|
||||
return -ENODEV;
|
||||
|
||||
@ -371,6 +394,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
|
||||
out_err:
|
||||
kfree(acomp);
|
||||
bus->audio_component = NULL;
|
||||
hdac_acomp = NULL;
|
||||
dev_info(dev, "failed to add i915 component master (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
@ -404,6 +428,7 @@ int snd_hdac_i915_exit(struct hdac_bus *bus)
|
||||
|
||||
kfree(acomp);
|
||||
bus->audio_component = NULL;
|
||||
hdac_acomp = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
|
||||
WARN_ON(count != channels);
|
||||
}
|
||||
|
||||
static int spk_mask_from_spk_alloc(int spk_alloc)
|
||||
{
|
||||
int i;
|
||||
int spk_mask = eld_speaker_allocation_bits[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
|
||||
if (spk_alloc & (1 << i))
|
||||
spk_mask |= eld_speaker_allocation_bits[i];
|
||||
}
|
||||
|
||||
return spk_mask;
|
||||
}
|
||||
|
||||
static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
unsigned int size, unsigned int __user *tlv)
|
||||
{
|
||||
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
|
||||
struct hdac_chmap *chmap = info->private_data;
|
||||
int pcm_idx = kcontrol->private_value;
|
||||
unsigned int __user *dst;
|
||||
int chs, count = 0;
|
||||
unsigned long max_chs;
|
||||
int type;
|
||||
int spk_alloc, spk_mask;
|
||||
|
||||
if (size < 8)
|
||||
return -ENOMEM;
|
||||
@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
return -EFAULT;
|
||||
size -= 8;
|
||||
dst = tlv + 2;
|
||||
for (chs = 2; chs <= chmap->channels_max; chs++) {
|
||||
|
||||
spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
|
||||
spk_mask = spk_mask_from_spk_alloc(spk_alloc);
|
||||
|
||||
max_chs = hweight_long(spk_mask);
|
||||
|
||||
for (chs = 2; chs <= max_chs; chs++) {
|
||||
int i;
|
||||
struct hdac_cea_channel_speaker_allocation *cap;
|
||||
|
||||
cap = channel_allocations;
|
||||
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
|
||||
int chs_bytes = chs * 4;
|
||||
int type = chmap->ops.chmap_cea_alloc_validate_get_type(
|
||||
chmap, cap, chs);
|
||||
unsigned int tlv_chmap[8];
|
||||
|
||||
if (type < 0)
|
||||
if (cap->channels != chs)
|
||||
continue;
|
||||
|
||||
if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
|
||||
continue;
|
||||
|
||||
type = chmap->ops.chmap_cea_alloc_validate_get_type(
|
||||
chmap, cap, chs);
|
||||
if (type < 0)
|
||||
return -ENODEV;
|
||||
if (size < 8)
|
||||
return -ENOMEM;
|
||||
|
||||
if (put_user(type, dst) ||
|
||||
put_user(chs_bytes, dst + 1))
|
||||
return -EFAULT;
|
||||
|
||||
dst += 2;
|
||||
size -= 8;
|
||||
count += 8;
|
||||
|
||||
if (size < chs_bytes)
|
||||
return -ENOMEM;
|
||||
|
||||
size -= chs_bytes;
|
||||
count += chs_bytes;
|
||||
chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
|
||||
tlv_chmap, chs);
|
||||
|
||||
if (copy_to_user(dst, tlv_chmap, chs_bytes))
|
||||
return -EFAULT;
|
||||
dst += chs;
|
||||
}
|
||||
}
|
||||
|
||||
if (put_user(count, tlv + 1))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -785,6 +785,9 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
|
||||
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
|
||||
header->number);
|
||||
|
||||
if (header->number >= ARRAY_SIZE(dev->patch_status))
|
||||
return -EINVAL;
|
||||
|
||||
dev->patch_status[header->number] |= WF_SLOT_FILLED;
|
||||
|
||||
bptr = buf;
|
||||
@ -809,6 +812,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
|
||||
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
|
||||
header->number);
|
||||
|
||||
if (header->number >= ARRAY_SIZE(dev->prog_status))
|
||||
return -EINVAL;
|
||||
|
||||
dev->prog_status[header->number] = WF_SLOT_USED;
|
||||
|
||||
/* XXX need to zero existing SLOT_USED bit for program_status[i]
|
||||
@ -898,6 +904,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
|
||||
header->number = x;
|
||||
}
|
||||
|
||||
if (header->number >= WF_MAX_SAMPLE)
|
||||
return -EINVAL;
|
||||
|
||||
if (header->size) {
|
||||
|
||||
/* XXX it's a debatable point whether or not RDONLY semantics
|
||||
|
@ -1414,11 +1414,9 @@ attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *m
|
||||
else {
|
||||
#ifdef CONFIG_ARCH_NETWINDER
|
||||
if (machine_is_netwinder()) {
|
||||
init_timer(&vnc_timer);
|
||||
vnc_timer.function = vnc_slider_tick;
|
||||
vnc_timer.expires = jiffies;
|
||||
vnc_timer.data = nr_waveartist_devs;
|
||||
add_timer(&vnc_timer);
|
||||
setup_timer(&vnc_timer, vnc_slider_tick,
|
||||
nr_waveartist_devs);
|
||||
mod_timer(&vnc_timer, jiffies);
|
||||
|
||||
vnc_configure_mixer(devc, 0);
|
||||
|
||||
|
@ -2151,8 +2151,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
|
||||
stream->resources, en,
|
||||
VORTEX_RESOURCE_SRC)) < 0) {
|
||||
memset(stream->resources, 0,
|
||||
sizeof(unsigned char) *
|
||||
VORTEX_RESOURCE_LAST);
|
||||
sizeof(stream->resources));
|
||||
return -EBUSY;
|
||||
}
|
||||
if (stream->type != VORTEX_PCM_A3D) {
|
||||
@ -2162,7 +2161,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
|
||||
VORTEX_RESOURCE_MIXIN)) < 0) {
|
||||
memset(stream->resources,
|
||||
0,
|
||||
sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
|
||||
sizeof(stream->resources));
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
@ -2175,8 +2174,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
|
||||
stream->resources, en,
|
||||
VORTEX_RESOURCE_A3D)) < 0) {
|
||||
memset(stream->resources, 0,
|
||||
sizeof(unsigned char) *
|
||||
VORTEX_RESOURCE_LAST);
|
||||
sizeof(stream->resources));
|
||||
dev_err(vortex->card->dev,
|
||||
"out of A3D sources. Sorry\n");
|
||||
return -EBUSY;
|
||||
@ -2290,8 +2288,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
|
||||
VORTEX_RESOURCE_MIXOUT))
|
||||
< 0) {
|
||||
memset(stream->resources, 0,
|
||||
sizeof(unsigned char) *
|
||||
VORTEX_RESOURCE_LAST);
|
||||
sizeof(stream->resources));
|
||||
return -EBUSY;
|
||||
}
|
||||
if ((src[i] =
|
||||
@ -2299,8 +2296,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
|
||||
stream->resources, en,
|
||||
VORTEX_RESOURCE_SRC)) < 0) {
|
||||
memset(stream->resources, 0,
|
||||
sizeof(unsigned char) *
|
||||
VORTEX_RESOURCE_LAST);
|
||||
sizeof(stream->resources));
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
@ -432,7 +432,10 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
|
||||
#endif
|
||||
//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
|
||||
spin_unlock(&chip->lock);
|
||||
return (bytes_to_frames(substream->runtime, current_ptr));
|
||||
current_ptr = bytes_to_frames(substream->runtime, current_ptr);
|
||||
if (current_ptr >= substream->runtime->buffer_size)
|
||||
current_ptr = 0;
|
||||
return current_ptr;
|
||||
}
|
||||
|
||||
/* operators */
|
||||
|
@ -49,7 +49,7 @@ struct ct_timer {
|
||||
spinlock_t lock; /* global timer lock (for xfitimer) */
|
||||
spinlock_t list_lock; /* lock for instance list */
|
||||
struct ct_atc *atc;
|
||||
struct ct_timer_ops *ops;
|
||||
const struct ct_timer_ops *ops;
|
||||
struct list_head instance_head;
|
||||
struct list_head running_head;
|
||||
unsigned int wc; /* current wallclock */
|
||||
@ -128,7 +128,7 @@ static void ct_systimer_prepare(struct ct_timer_instance *ti)
|
||||
|
||||
#define ct_systimer_free ct_systimer_prepare
|
||||
|
||||
static struct ct_timer_ops ct_systimer_ops = {
|
||||
static const struct ct_timer_ops ct_systimer_ops = {
|
||||
.init = ct_systimer_init,
|
||||
.free_instance = ct_systimer_free,
|
||||
.prepare = ct_systimer_prepare,
|
||||
@ -322,7 +322,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer)
|
||||
ct_xfitimer_irq_stop(atimer);
|
||||
}
|
||||
|
||||
static struct ct_timer_ops ct_xfitimer_ops = {
|
||||
static const struct ct_timer_ops ct_xfitimer_ops = {
|
||||
.prepare = ct_xfitimer_prepare,
|
||||
.start = ct_xfitimer_start,
|
||||
.stop = ct_xfitimer_stop,
|
||||
|
@ -1548,7 +1548,7 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
|
||||
int val = 0;
|
||||
|
||||
spin_lock_irq(&ensoniq->reg_lock);
|
||||
if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
|
||||
if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
|
||||
val = 1;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
spin_unlock_irq(&ensoniq->reg_lock);
|
||||
|
@ -50,9 +50,13 @@ config SND_HDA_RECONFIG
|
||||
bool "Allow dynamic codec reconfiguration"
|
||||
help
|
||||
Say Y here to enable the HD-audio codec re-configuration feature.
|
||||
This adds the sysfs interfaces to allow user to clear the whole
|
||||
codec configuration, change the codec setup, add extra verbs,
|
||||
and re-configure the codec dynamically.
|
||||
It allows user to clear the whole codec configuration, change the
|
||||
codec setup, add extra verbs, and re-configure the codec dynamically.
|
||||
|
||||
Note that this item alone doesn't provide the sysfs interface, but
|
||||
enables the feature just for the patch loader below.
|
||||
If you need the traditional sysfs entries for the manual interaction,
|
||||
turn on CONFIG_SND_HDA_HWDEP as well.
|
||||
|
||||
config SND_HDA_INPUT_BEEP
|
||||
bool "Support digital beep via input layer"
|
||||
|
@ -5434,6 +5434,7 @@ static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
spec->cur_adc_stream_tag = stream_tag;
|
||||
spec->cur_adc_format = format;
|
||||
snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
|
||||
call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5444,6 +5445,7 @@ static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
|
||||
spec->cur_adc = 0;
|
||||
call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,9 @@ struct hdmi_ops {
|
||||
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
|
||||
hda_nid_t pin_nid, u32 stream_tag, int format);
|
||||
|
||||
void (*pin_cvt_fixup)(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
hda_nid_t cvt_nid);
|
||||
};
|
||||
|
||||
struct hdmi_pcm {
|
||||
@ -684,7 +687,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
||||
if (!channels)
|
||||
return;
|
||||
|
||||
if (is_haswell_plus(codec))
|
||||
/* some HW (e.g. HSW+) needs reprogramming the amp at each time */
|
||||
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_OUT_UNMUTE);
|
||||
@ -864,9 +868,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int err;
|
||||
|
||||
if (is_haswell_plus(codec))
|
||||
haswell_verify_D0(codec, cvt_nid, pin_nid);
|
||||
|
||||
err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
|
||||
|
||||
if (err) {
|
||||
@ -884,7 +885,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
||||
* of the pin.
|
||||
*/
|
||||
static int hdmi_choose_cvt(struct hda_codec *codec,
|
||||
int pin_idx, int *cvt_id, int *mux_id)
|
||||
int pin_idx, int *cvt_id)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin;
|
||||
@ -925,8 +926,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
|
||||
|
||||
if (cvt_id)
|
||||
*cvt_id = cvt_idx;
|
||||
if (mux_id)
|
||||
*mux_id = mux_idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1019,9 +1018,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
|
||||
int mux_idx;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
|
||||
return;
|
||||
|
||||
/* On Intel platform, the mapping of converter nid to
|
||||
* mux index of the pins are always the same.
|
||||
* The pin nid may be 0, this means all pins will not
|
||||
@ -1032,6 +1028,17 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
|
||||
intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
|
||||
}
|
||||
|
||||
/* skeleton caller of pin_cvt_fixup ops */
|
||||
static void pin_cvt_fixup(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
hda_nid_t cvt_nid)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec->ops.pin_cvt_fixup)
|
||||
spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid);
|
||||
}
|
||||
|
||||
/* called in hdmi_pcm_open when no pin is assigned to the PCM
|
||||
* in dyn_pcm_assign mode.
|
||||
*/
|
||||
@ -1049,7 +1056,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
|
||||
if (pcm_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
|
||||
err = hdmi_choose_cvt(codec, -1, &cvt_idx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1057,7 +1064,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
|
||||
per_cvt->assigned = 1;
|
||||
hinfo->nid = per_cvt->cvt_nid;
|
||||
|
||||
intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
|
||||
pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid);
|
||||
|
||||
set_bit(pcm_idx, &spec->pcm_in_use);
|
||||
/* todo: setup spdif ctls assign */
|
||||
@ -1089,7 +1096,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
|
||||
int pin_idx, cvt_idx, pcm_idx;
|
||||
struct hdmi_spec_per_pin *per_pin;
|
||||
struct hdmi_eld *eld;
|
||||
struct hdmi_spec_per_cvt *per_cvt = NULL;
|
||||
@ -1118,7 +1125,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
}
|
||||
}
|
||||
|
||||
err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
|
||||
err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
return err;
|
||||
@ -1135,11 +1142,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
|
||||
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
|
||||
AC_VERB_SET_CONNECT_SEL,
|
||||
mux_idx);
|
||||
per_pin->mux_idx);
|
||||
|
||||
/* configure unused pins to choose other converters */
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
|
||||
intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
|
||||
pin_cvt_fixup(codec, per_pin, 0);
|
||||
|
||||
snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
|
||||
|
||||
@ -1372,12 +1378,7 @@ static void update_eld(struct hda_codec *codec,
|
||||
* and this can make HW reset converter selection on a pin.
|
||||
*/
|
||||
if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
|
||||
intel_verify_pin_cvt_connect(codec, per_pin);
|
||||
intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
|
||||
per_pin->mux_idx);
|
||||
}
|
||||
|
||||
pin_cvt_fixup(codec, per_pin, 0);
|
||||
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
|
||||
}
|
||||
|
||||
@ -1484,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
||||
|
||||
mutex_lock(&per_pin->lock);
|
||||
eld->monitor_present = false;
|
||||
size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
|
||||
size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
|
||||
&eld->monitor_present, eld->eld_buffer,
|
||||
ELD_MAX_SIZE);
|
||||
if (size > 0) {
|
||||
@ -1711,7 +1712,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
* skip pin setup and return 0 to make audio playback
|
||||
* be ongoing
|
||||
*/
|
||||
intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
|
||||
pin_cvt_fixup(codec, NULL, cvt_nid);
|
||||
snd_hda_codec_setup_stream(codec, cvt_nid,
|
||||
stream_tag, 0, format);
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
@ -1724,23 +1725,21 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
}
|
||||
per_pin = get_pin(spec, pin_idx);
|
||||
pin_nid = per_pin->pin_nid;
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
|
||||
/* Verify pin:cvt selections to avoid silent audio after S3.
|
||||
* After S3, the audio driver restores pin:cvt selections
|
||||
* but this can happen before gfx is ready and such selection
|
||||
* is overlooked by HW. Thus multiple pins can share a same
|
||||
* default convertor and mute control will affect each other,
|
||||
* which can cause a resumed audio playback become silent
|
||||
* after S3.
|
||||
*/
|
||||
intel_verify_pin_cvt_connect(codec, per_pin);
|
||||
intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
|
||||
}
|
||||
|
||||
/* Verify pin:cvt selections to avoid silent audio after S3.
|
||||
* After S3, the audio driver restores pin:cvt selections
|
||||
* but this can happen before gfx is ready and such selection
|
||||
* is overlooked by HW. Thus multiple pins can share a same
|
||||
* default convertor and mute control will affect each other,
|
||||
* which can cause a resumed audio playback become silent
|
||||
* after S3.
|
||||
*/
|
||||
pin_cvt_fixup(codec, per_pin, 0);
|
||||
|
||||
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
|
||||
/* Todo: add DP1.2 MST audio support later */
|
||||
if (codec_has_acomp(codec))
|
||||
snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
|
||||
snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate);
|
||||
|
||||
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
|
||||
mutex_lock(&per_pin->lock);
|
||||
@ -1837,6 +1836,18 @@ static const struct hda_pcm_ops generic_ops = {
|
||||
.cleanup = generic_hdmi_playback_pcm_cleanup,
|
||||
};
|
||||
|
||||
static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
|
||||
|
||||
if (!per_pin)
|
||||
return 0;
|
||||
|
||||
return per_pin->sink_eld.info.spk_alloc;
|
||||
}
|
||||
|
||||
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
|
||||
unsigned char *chmap)
|
||||
{
|
||||
@ -2075,6 +2086,20 @@ static void hdmi_array_free(struct hdmi_spec *spec)
|
||||
snd_array_free(&spec->cvts);
|
||||
}
|
||||
|
||||
static void generic_spec_free(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
if (spec) {
|
||||
if (spec->i915_bound)
|
||||
snd_hdac_i915_exit(&codec->bus->core);
|
||||
hdmi_array_free(spec);
|
||||
kfree(spec);
|
||||
codec->spec = NULL;
|
||||
}
|
||||
codec->dp_mst = false;
|
||||
}
|
||||
|
||||
static void generic_hdmi_free(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
@ -2099,10 +2124,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
||||
spec->pcm_rec[pcm_idx].jack = NULL;
|
||||
}
|
||||
|
||||
if (spec->i915_bound)
|
||||
snd_hdac_i915_exit(&codec->bus->core);
|
||||
hdmi_array_free(spec);
|
||||
kfree(spec);
|
||||
generic_spec_free(codec);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -2140,6 +2162,55 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
|
||||
.setup_stream = hdmi_setup_stream,
|
||||
};
|
||||
|
||||
/* allocate codec->spec and assign/initialize generic parser ops */
|
||||
static int alloc_generic_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->ops = generic_standard_hdmi_ops;
|
||||
mutex_init(&spec->pcm_lock);
|
||||
snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
|
||||
|
||||
spec->chmap.ops.get_chmap = hdmi_get_chmap;
|
||||
spec->chmap.ops.set_chmap = hdmi_set_chmap;
|
||||
spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
|
||||
spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc,
|
||||
|
||||
codec->spec = spec;
|
||||
hdmi_array_init(spec, 4);
|
||||
|
||||
codec->patch_ops = generic_hdmi_patch_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generic HDMI parser */
|
||||
static int patch_generic_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = alloc_generic_hdmi(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = hdmi_parse_codec(codec);
|
||||
if (err < 0) {
|
||||
generic_spec_free(codec);
|
||||
return err;
|
||||
}
|
||||
|
||||
generic_hdmi_init_per_pins(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Intel codec parsers and helpers
|
||||
*/
|
||||
|
||||
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
@ -2217,12 +2288,23 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
||||
static void intel_pin_eld_notify(void *audio_ptr, int port)
|
||||
{
|
||||
struct hda_codec *codec = audio_ptr;
|
||||
int pin_nid = port + 0x04;
|
||||
int pin_nid;
|
||||
|
||||
/* we assume only from port-B to port-D */
|
||||
if (port < 1 || port > 3)
|
||||
return;
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x80860054: /* ILK */
|
||||
case 0x80862804: /* ILK */
|
||||
case 0x80862882: /* VLV */
|
||||
pin_nid = port + 0x03;
|
||||
break;
|
||||
default:
|
||||
pin_nid = port + 0x04;
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip notification during system suspend (but not in runtime PM);
|
||||
* the state will be updated at resume
|
||||
*/
|
||||
@ -2236,95 +2318,161 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
|
||||
check_presence_and_report(codec, pin_nid);
|
||||
}
|
||||
|
||||
static int patch_generic_hdmi(struct hda_codec *codec)
|
||||
/* register i915 component pin_eld_notify callback */
|
||||
static void register_i915_notifier(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->use_acomp_notifier = true;
|
||||
spec->i915_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
|
||||
}
|
||||
|
||||
/* setup_stream ops override for HSW+ */
|
||||
static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
||||
hda_nid_t pin_nid, u32 stream_tag, int format)
|
||||
{
|
||||
haswell_verify_D0(codec, cvt_nid, pin_nid);
|
||||
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
|
||||
}
|
||||
|
||||
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
|
||||
static void i915_pin_cvt_fixup(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
hda_nid_t cvt_nid)
|
||||
{
|
||||
if (per_pin) {
|
||||
intel_verify_pin_cvt_connect(codec, per_pin);
|
||||
intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
|
||||
per_pin->mux_idx);
|
||||
} else {
|
||||
intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Intel Haswell and onwards; audio component with eld notifier */
|
||||
static int patch_i915_hsw_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int err;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->ops = generic_standard_hdmi_ops;
|
||||
mutex_init(&spec->pcm_lock);
|
||||
snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
|
||||
|
||||
spec->chmap.ops.get_chmap = hdmi_get_chmap;
|
||||
spec->chmap.ops.set_chmap = hdmi_set_chmap;
|
||||
spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
|
||||
|
||||
codec->spec = spec;
|
||||
hdmi_array_init(spec, 4);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_I915
|
||||
/* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
|
||||
if ((codec->core.vendor_id >> 16) == 0x8086 &&
|
||||
is_haswell_plus(codec)) {
|
||||
#if 0
|
||||
/* on-demand binding leads to an unbalanced refcount when
|
||||
* both i915 and hda drivers are probed concurrently;
|
||||
* disabled temporarily for now
|
||||
*/
|
||||
if (!codec->bus->core.audio_component)
|
||||
if (!snd_hdac_i915_init(&codec->bus->core))
|
||||
spec->i915_bound = true;
|
||||
#endif
|
||||
/* use i915 audio component notifier for hotplug */
|
||||
if (codec->bus->core.audio_component)
|
||||
spec->use_acomp_notifier = true;
|
||||
/* HSW+ requires i915 binding */
|
||||
if (!codec->bus->core.audio_component) {
|
||||
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_haswell_plus(codec)) {
|
||||
intel_haswell_enable_all_pins(codec, true);
|
||||
intel_haswell_fixup_enable_dp12(codec);
|
||||
err = alloc_generic_hdmi(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec = codec->spec;
|
||||
|
||||
intel_haswell_enable_all_pins(codec, true);
|
||||
intel_haswell_fixup_enable_dp12(codec);
|
||||
|
||||
/* For Haswell/Broadwell, the controller is also in the power well and
|
||||
* can cover the codec power request, and so need not set this flag.
|
||||
*/
|
||||
if (!is_haswell(codec) && !is_broadwell(codec))
|
||||
codec->core.link_power_control = 1;
|
||||
|
||||
codec->patch_ops.set_power_state = haswell_set_power_state;
|
||||
codec->dp_mst = true;
|
||||
codec->depop_delay = 0;
|
||||
codec->auto_runtime_pm = 1;
|
||||
|
||||
spec->ops.setup_stream = i915_hsw_setup_stream;
|
||||
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
|
||||
|
||||
err = hdmi_parse_codec(codec);
|
||||
if (err < 0) {
|
||||
generic_spec_free(codec);
|
||||
return err;
|
||||
}
|
||||
|
||||
generic_hdmi_init_per_pins(codec);
|
||||
register_i915_notifier(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Intel Baytrail and Braswell; with eld notifier */
|
||||
static int patch_i915_byt_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int err;
|
||||
|
||||
/* requires i915 binding */
|
||||
if (!codec->bus->core.audio_component) {
|
||||
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = alloc_generic_hdmi(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec = codec->spec;
|
||||
|
||||
/* For Valleyview/Cherryview, only the display codec is in the display
|
||||
* power well and can use link_power ops to request/release the power.
|
||||
* For Haswell/Broadwell, the controller is also in the power well and
|
||||
* can cover the codec power request, and so need not set this flag.
|
||||
* For previous platforms, there is no such power well feature.
|
||||
*/
|
||||
if (is_valleyview_plus(codec) || is_skylake(codec) ||
|
||||
is_broxton(codec))
|
||||
codec->core.link_power_control = 1;
|
||||
codec->core.link_power_control = 1;
|
||||
|
||||
if (hdmi_parse_codec(codec) < 0) {
|
||||
if (spec->i915_bound)
|
||||
snd_hdac_i915_exit(&codec->bus->core);
|
||||
codec->spec = NULL;
|
||||
kfree(spec);
|
||||
return -EINVAL;
|
||||
}
|
||||
codec->patch_ops = generic_hdmi_patch_ops;
|
||||
if (is_haswell_plus(codec)) {
|
||||
codec->patch_ops.set_power_state = haswell_set_power_state;
|
||||
codec->dp_mst = true;
|
||||
}
|
||||
codec->depop_delay = 0;
|
||||
codec->auto_runtime_pm = 1;
|
||||
|
||||
/* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
|
||||
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
|
||||
codec->auto_runtime_pm = 1;
|
||||
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
|
||||
|
||||
err = hdmi_parse_codec(codec);
|
||||
if (err < 0) {
|
||||
generic_spec_free(codec);
|
||||
return err;
|
||||
}
|
||||
|
||||
generic_hdmi_init_per_pins(codec);
|
||||
|
||||
|
||||
if (codec_has_acomp(codec)) {
|
||||
codec->depop_delay = 0;
|
||||
spec->i915_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
|
||||
}
|
||||
|
||||
WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
|
||||
register_i915_notifier(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */
|
||||
static int patch_i915_cpt_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int err;
|
||||
|
||||
/* no i915 component should have been bound before this */
|
||||
if (WARN_ON(codec->bus->core.audio_component))
|
||||
return -EBUSY;
|
||||
|
||||
err = alloc_generic_hdmi(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec = codec->spec;
|
||||
|
||||
/* Try to bind with i915 now */
|
||||
err = snd_hdac_i915_init(&codec->bus->core);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
spec->i915_bound = true;
|
||||
|
||||
err = hdmi_parse_codec(codec);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
generic_hdmi_init_per_pins(codec);
|
||||
register_i915_notifier(codec);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
generic_spec_free(codec);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shared non-generic implementations
|
||||
*/
|
||||
@ -3492,21 +3640,21 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
|
||||
HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
|
||||
HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
|
||||
/* special ID for generic HDMI */
|
||||
HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
|
||||
|
@ -342,6 +342,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
|
||||
case 0x10ec0293:
|
||||
alc_update_coef_idx(codec, 0xa, 1<<13, 0);
|
||||
break;
|
||||
case 0x10ec0234:
|
||||
case 0x10ec0274:
|
||||
case 0x10ec0294:
|
||||
alc_update_coef_idx(codec, 0x10, 1<<15, 0);
|
||||
break;
|
||||
case 0x10ec0662:
|
||||
if ((coef & 0x00f0) == 0x0030)
|
||||
alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
|
||||
@ -2647,6 +2652,7 @@ enum {
|
||||
ALC269_TYPE_ALC255,
|
||||
ALC269_TYPE_ALC256,
|
||||
ALC269_TYPE_ALC225,
|
||||
ALC269_TYPE_ALC294,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2677,6 +2683,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
|
||||
case ALC269_TYPE_ALC255:
|
||||
case ALC269_TYPE_ALC256:
|
||||
case ALC269_TYPE_ALC225:
|
||||
case ALC269_TYPE_ALC294:
|
||||
ssids = alc269_ssids;
|
||||
break;
|
||||
default:
|
||||
@ -6028,6 +6035,11 @@ static int patch_alc269(struct hda_codec *codec)
|
||||
case 0x10ec0225:
|
||||
spec->codec_variant = ALC269_TYPE_ALC225;
|
||||
break;
|
||||
case 0x10ec0234:
|
||||
case 0x10ec0274:
|
||||
case 0x10ec0294:
|
||||
spec->codec_variant = ALC269_TYPE_ALC294;
|
||||
break;
|
||||
}
|
||||
|
||||
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
|
||||
@ -6942,6 +6954,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
|
||||
HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
|
||||
@ -6952,6 +6965,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
|
||||
HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
|
||||
@ -6964,6 +6978,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
|
||||
HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
|
||||
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
|
||||
|
@ -42,12 +42,6 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#ifdef CONFIG_KVM_GUEST
|
||||
#include <linux/kvm_para.h>
|
||||
#else
|
||||
#define kvm_para_available() (0)
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -2972,25 +2966,17 @@ static int snd_intel8x0_inside_vm(struct pci_dev *pci)
|
||||
goto fini;
|
||||
}
|
||||
|
||||
/* detect KVM and Parallels virtual environments */
|
||||
result = kvm_para_available();
|
||||
#ifdef X86_FEATURE_HYPERVISOR
|
||||
result = result || boot_cpu_has(X86_FEATURE_HYPERVISOR);
|
||||
#endif
|
||||
if (!result)
|
||||
goto fini;
|
||||
|
||||
/* check for known (emulated) devices */
|
||||
result = 0;
|
||||
if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
|
||||
pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
|
||||
/* KVM emulated sound, PCI SSID: 1af4:1100 */
|
||||
msg = "enable KVM";
|
||||
result = 1;
|
||||
} else if (pci->subsystem_vendor == 0x1ab8) {
|
||||
/* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
|
||||
msg = "enable Parallels VM";
|
||||
} else {
|
||||
msg = "disable (unknown or VT-d) VM";
|
||||
result = 0;
|
||||
result = 1;
|
||||
}
|
||||
|
||||
fini:
|
||||
|
@ -644,7 +644,7 @@ static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (current_state == state)
|
||||
if (!err && current_state == state)
|
||||
return 0;
|
||||
|
||||
mdelay(1);
|
||||
|
@ -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,8 @@ 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;
|
||||
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 +1743,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 +1754,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 +1764,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 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
|
||||
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),
|
||||
},
|
||||
};
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user