mirror of
https://github.com/fail0verflow/switch-linux.git
synced 2025-05-04 02:34:21 -04:00
Merge remote-tracking branches 'asoc/topic/ab8500', 'asoc/topic/arizona', 'asoc/topic/atmel', 'asoc/topic/bcm' and 'asoc/topic/bitfield' into asoc-next
This commit is contained in:
commit
ffe9c4f330
22 changed files with 1312 additions and 285 deletions
|
@ -0,0 +1,88 @@
|
||||||
|
Devicetree bindings for the Axentia TSE-850 audio complex
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "axentia,tse850-pcm5142"
|
||||||
|
- axentia,ssc-controller: The phandle of the atmel SSC controller used as
|
||||||
|
cpu dai.
|
||||||
|
- axentia,audio-codec: The phandle of the PCM5142 codec.
|
||||||
|
- axentia,add-gpios: gpio specifier that controls the mixer.
|
||||||
|
- axentia,loop1-gpios: gpio specifier that controls loop relays on channel 1.
|
||||||
|
- axentia,loop2-gpios: gpio specifier that controls loop relays on channel 2.
|
||||||
|
- axentia,ana-supply: Regulator that supplies the output amplifier. Must
|
||||||
|
support voltages in the 2V - 20V range, in 1V steps.
|
||||||
|
|
||||||
|
The schematics explaining the gpios are as follows:
|
||||||
|
|
||||||
|
loop1 relays
|
||||||
|
IN1 +---o +------------+ o---+ OUT1
|
||||||
|
\ /
|
||||||
|
+ +
|
||||||
|
| / |
|
||||||
|
+--o +--. |
|
||||||
|
| add | |
|
||||||
|
| V |
|
||||||
|
| .---. |
|
||||||
|
DAC +----------->|Sum|---+
|
||||||
|
| '---' |
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
|
||||||
|
IN2 +---o--+------------+--o---+ OUT2
|
||||||
|
loop2 relays
|
||||||
|
|
||||||
|
The 'loop1' gpio pin controlls two relays, which are either in loop position,
|
||||||
|
meaning that input and output are directly connected, or they are in mixer
|
||||||
|
position, meaning that the signal is passed through the 'Sum' mixer. Similarly
|
||||||
|
for 'loop2'.
|
||||||
|
|
||||||
|
In the above, the 'loop1' relays are inactive, thus feeding IN1 to the mixer
|
||||||
|
(if 'add' is active) and feeding the mixer output to OUT1. The 'loop2' relays
|
||||||
|
are active, short-cutting the TSE-850 from channel 2. IN1, IN2, OUT1 and OUT2
|
||||||
|
are TSE-850 connectors and DAC is the PCB name of the (filtered) output from
|
||||||
|
the PCM5142 codec.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
&i2c {
|
||||||
|
codec: pcm5142@4c {
|
||||||
|
compatible = "ti,pcm5142";
|
||||||
|
|
||||||
|
reg = <0x4c>;
|
||||||
|
|
||||||
|
AVDD-supply = <®_3v3>;
|
||||||
|
DVDD-supply = <®_3v3>;
|
||||||
|
CPVDD-supply = <®_3v3>;
|
||||||
|
|
||||||
|
clocks = <&sck>;
|
||||||
|
|
||||||
|
pll-in = <3>;
|
||||||
|
pll-out = <6>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ana: ana-reg {
|
||||||
|
compatible = "pwm-regulator";
|
||||||
|
|
||||||
|
regulator-name = "ANA";
|
||||||
|
|
||||||
|
pwms = <&pwm0 2 1000 PWM_POLARITY_INVERTED>;
|
||||||
|
pwm-dutycycle-unit = <1000>;
|
||||||
|
pwm-dutycycle-range = <100 1000>;
|
||||||
|
|
||||||
|
regulator-min-microvolt = <2000000>;
|
||||||
|
regulator-max-microvolt = <20000000>;
|
||||||
|
regulator-ramp-delay = <1000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
sound {
|
||||||
|
compatible = "axentia,tse850-pcm5142";
|
||||||
|
|
||||||
|
axentia,ssc-controller = <&ssc0>;
|
||||||
|
axentia,audio-codec = <&codec>;
|
||||||
|
|
||||||
|
axentia,add-gpios = <&pioA 8 GPIO_ACTIVE_LOW>;
|
||||||
|
axentia,loop1-gpios = <&pioA 10 GPIO_ACTIVE_LOW>;
|
||||||
|
axentia,loop2-gpios = <&pioA 11 GPIO_ACTIVE_LOW>;
|
||||||
|
|
||||||
|
axentia,ana-supply = <&ana>;
|
||||||
|
};
|
|
@ -2325,6 +2325,13 @@ F: include/uapi/linux/ax25.h
|
||||||
F: include/net/ax25.h
|
F: include/net/ax25.h
|
||||||
F: net/ax25/
|
F: net/ax25/
|
||||||
|
|
||||||
|
AXENTIA ASOC DRIVERS
|
||||||
|
M: Peter Rosin <peda@axentia.se>
|
||||||
|
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/sound/axentia,*
|
||||||
|
F: sound/soc/atmel/tse850-pcm5142.c
|
||||||
|
|
||||||
AZ6007 DVB DRIVER
|
AZ6007 DVB DRIVER
|
||||||
M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
|
M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
|
||||||
M: Mauro Carvalho Chehab <mchehab@kernel.org>
|
M: Mauro Carvalho Chehab <mchehab@kernel.org>
|
||||||
|
|
|
@ -268,8 +268,9 @@ struct snd_soc_dai {
|
||||||
unsigned int symmetric_rates:1;
|
unsigned int symmetric_rates:1;
|
||||||
unsigned int symmetric_channels:1;
|
unsigned int symmetric_channels:1;
|
||||||
unsigned int symmetric_samplebits:1;
|
unsigned int symmetric_samplebits:1;
|
||||||
|
unsigned int probed:1;
|
||||||
|
|
||||||
unsigned int active;
|
unsigned int active;
|
||||||
unsigned char probed:1;
|
|
||||||
|
|
||||||
struct snd_soc_dapm_widget *playback_widget;
|
struct snd_soc_dapm_widget *playback_widget;
|
||||||
struct snd_soc_dapm_widget *capture_widget;
|
struct snd_soc_dapm_widget *capture_widget;
|
||||||
|
|
|
@ -1029,13 +1029,13 @@ struct snd_soc_dai_link {
|
||||||
const struct snd_soc_ops *ops;
|
const struct snd_soc_ops *ops;
|
||||||
const struct snd_soc_compr_ops *compr_ops;
|
const struct snd_soc_compr_ops *compr_ops;
|
||||||
|
|
||||||
/* For unidirectional dai links */
|
|
||||||
bool playback_only;
|
|
||||||
bool capture_only;
|
|
||||||
|
|
||||||
/* Mark this pcm with non atomic ops */
|
/* Mark this pcm with non atomic ops */
|
||||||
bool nonatomic;
|
bool nonatomic;
|
||||||
|
|
||||||
|
/* For unidirectional dai links */
|
||||||
|
unsigned int playback_only:1;
|
||||||
|
unsigned int capture_only:1;
|
||||||
|
|
||||||
/* Keep DAI active over suspend */
|
/* Keep DAI active over suspend */
|
||||||
unsigned int ignore_suspend:1;
|
unsigned int ignore_suspend:1;
|
||||||
|
|
||||||
|
@ -1206,14 +1206,11 @@ struct snd_soc_pcm_runtime {
|
||||||
enum snd_soc_pcm_subclass pcm_subclass;
|
enum snd_soc_pcm_subclass pcm_subclass;
|
||||||
struct snd_pcm_ops ops;
|
struct snd_pcm_ops ops;
|
||||||
|
|
||||||
unsigned int dev_registered:1;
|
|
||||||
|
|
||||||
/* Dynamic PCM BE runtime data */
|
/* Dynamic PCM BE runtime data */
|
||||||
struct snd_soc_dpcm_runtime dpcm[2];
|
struct snd_soc_dpcm_runtime dpcm[2];
|
||||||
int fe_compr;
|
int fe_compr;
|
||||||
|
|
||||||
long pmdown_time;
|
long pmdown_time;
|
||||||
unsigned char pop_wait:1;
|
|
||||||
|
|
||||||
/* runtime devices */
|
/* runtime devices */
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
|
@ -1234,6 +1231,10 @@ struct snd_soc_pcm_runtime {
|
||||||
|
|
||||||
unsigned int num; /* 0-based and monotonic increasing */
|
unsigned int num; /* 0-based and monotonic increasing */
|
||||||
struct list_head list; /* rtd list of the soc card */
|
struct list_head list; /* rtd list of the soc card */
|
||||||
|
|
||||||
|
/* bit field */
|
||||||
|
unsigned int dev_registered:1;
|
||||||
|
unsigned int pop_wait:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* mixer control */
|
/* mixer control */
|
||||||
|
|
|
@ -78,4 +78,14 @@ config SND_ATMEL_SOC_PDMIC
|
||||||
help
|
help
|
||||||
Say Y if you want to add support for Atmel ASoC driver for boards using
|
Say Y if you want to add support for Atmel ASoC driver for boards using
|
||||||
PDMIC.
|
PDMIC.
|
||||||
|
|
||||||
|
config SND_ATMEL_SOC_TSE850_PCM5142
|
||||||
|
tristate "ASoC driver for the Axentia TSE-850"
|
||||||
|
depends on ARCH_AT91 && OF
|
||||||
|
depends on ATMEL_SSC && I2C
|
||||||
|
select SND_ATMEL_SOC_SSC_DMA
|
||||||
|
select SND_SOC_PCM512x_I2C
|
||||||
|
help
|
||||||
|
Say Y if you want to add support for the ASoC driver for the
|
||||||
|
Axentia TSE-850 with a PCM5142 codec.
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -13,9 +13,11 @@ snd-atmel-soc-wm8904-objs := atmel_wm8904.o
|
||||||
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
|
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
|
||||||
snd-atmel-soc-classd-objs := atmel-classd.o
|
snd-atmel-soc-classd-objs := atmel-classd.o
|
||||||
snd-atmel-soc-pdmic-objs := atmel-pdmic.o
|
snd-atmel-soc-pdmic-objs := atmel-pdmic.o
|
||||||
|
snd-atmel-soc-tse850-pcm5142-objs := tse850-pcm5142.o
|
||||||
|
|
||||||
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
|
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
|
||||||
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
|
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
|
||||||
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
|
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
|
||||||
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
|
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
|
||||||
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
|
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
|
||||||
|
obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o
|
||||||
|
|
|
@ -380,6 +380,7 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
|
||||||
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
|
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
|
||||||
/* Clear the SSC dividers */
|
/* Clear the SSC dividers */
|
||||||
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
|
ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
|
||||||
|
ssc_p->forced_divider = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&ssc_p->lock);
|
spin_unlock_irq(&ssc_p->lock);
|
||||||
|
|
||||||
|
@ -426,14 +427,17 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||||
else
|
else
|
||||||
if (div != ssc_p->cmr_div)
|
if (div != ssc_p->cmr_div)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ATMEL_SSC_TCMR_PERIOD:
|
case ATMEL_SSC_TCMR_PERIOD:
|
||||||
ssc_p->tcmr_period = div;
|
ssc_p->tcmr_period = div;
|
||||||
|
ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ATMEL_SSC_RCMR_PERIOD:
|
case ATMEL_SSC_RCMR_PERIOD:
|
||||||
ssc_p->rcmr_period = div;
|
ssc_p->rcmr_period = div;
|
||||||
|
ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -443,6 +447,28 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is the cpu-dai master of the frame clock? */
|
||||||
|
static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
|
||||||
|
{
|
||||||
|
switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_CBM_CFS:
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFS:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is the cpu-dai master of the bit clock? */
|
||||||
|
static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p)
|
||||||
|
{
|
||||||
|
switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFM:
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFS:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure the SSC.
|
* Configure the SSC.
|
||||||
*/
|
*/
|
||||||
|
@ -459,6 +485,9 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
u32 tfmr, rfmr, tcmr, rcmr;
|
u32 tfmr, rfmr, tcmr, rcmr;
|
||||||
int ret;
|
int ret;
|
||||||
int fslen, fslen_ext;
|
int fslen, fslen_ext;
|
||||||
|
u32 cmr_div;
|
||||||
|
u32 tcmr_period;
|
||||||
|
u32 rcmr_period;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Currently, there is only one set of dma params for
|
* Currently, there is only one set of dma params for
|
||||||
|
@ -470,6 +499,46 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
else
|
else
|
||||||
dir = 1;
|
dir = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the cpu dai should provide BCLK, but noone has provided the
|
||||||
|
* divider needed for that to work, fall back to something sensible.
|
||||||
|
*/
|
||||||
|
cmr_div = ssc_p->cmr_div;
|
||||||
|
if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) &&
|
||||||
|
atmel_ssc_cbs(ssc_p)) {
|
||||||
|
int bclk_rate = snd_soc_params_to_bclk(params);
|
||||||
|
|
||||||
|
if (bclk_rate < 0) {
|
||||||
|
dev_err(dai->dev, "unable to calculate cmr_div: %d\n",
|
||||||
|
bclk_rate);
|
||||||
|
return bclk_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the cpu dai should provide LRCLK, but noone has provided the
|
||||||
|
* dividers needed for that to work, fall back to something sensible.
|
||||||
|
*/
|
||||||
|
tcmr_period = ssc_p->tcmr_period;
|
||||||
|
rcmr_period = ssc_p->rcmr_period;
|
||||||
|
if (atmel_ssc_cfs(ssc_p)) {
|
||||||
|
int frame_size = snd_soc_params_to_frame_size(params);
|
||||||
|
|
||||||
|
if (frame_size < 0) {
|
||||||
|
dev_err(dai->dev,
|
||||||
|
"unable to calculate tx/rx cmr_period: %d\n",
|
||||||
|
frame_size);
|
||||||
|
return frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD)))
|
||||||
|
tcmr_period = frame_size / 2 - 1;
|
||||||
|
if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD)))
|
||||||
|
rcmr_period = frame_size / 2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
dma_params = ssc_p->dma_params[dir];
|
dma_params = ssc_p->dma_params[dir];
|
||||||
|
|
||||||
channels = params_channels(params);
|
channels = params_channels(params);
|
||||||
|
@ -524,7 +593,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
fslen_ext = (bits - 1) / 16;
|
fslen_ext = (bits - 1) / 16;
|
||||||
fslen = (bits - 1) % 16;
|
fslen = (bits - 1) % 16;
|
||||||
|
|
||||||
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
|
rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
|
||||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||||
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
|
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
|
||||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
||||||
|
@ -540,7 +609,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
| SSC_BF(RFMR_LOOP, 0)
|
| SSC_BF(RFMR_LOOP, 0)
|
||||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||||
|
|
||||||
tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
|
tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
|
||||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||||
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
|
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
|
||||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||||
|
@ -606,7 +675,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
fslen_ext = (bits - 1) / 16;
|
fslen_ext = (bits - 1) / 16;
|
||||||
fslen = (bits - 1) % 16;
|
fslen = (bits - 1) % 16;
|
||||||
|
|
||||||
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
|
rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
|
||||||
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
| SSC_BF(RCMR_STTDLY, START_DELAY)
|
||||||
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
|
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
|
||||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
||||||
|
@ -623,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
| SSC_BF(RFMR_LOOP, 0)
|
| SSC_BF(RFMR_LOOP, 0)
|
||||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||||
|
|
||||||
tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
|
tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
|
||||||
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
| SSC_BF(TCMR_STTDLY, START_DELAY)
|
||||||
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
|
| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
|
||||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||||
|
@ -650,7 +719,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
* MCK divider, and the BCLK signal is output
|
* MCK divider, and the BCLK signal is output
|
||||||
* on the SSC TK line.
|
* on the SSC TK line.
|
||||||
*/
|
*/
|
||||||
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
|
rcmr = SSC_BF(RCMR_PERIOD, rcmr_period)
|
||||||
| SSC_BF(RCMR_STTDLY, 1)
|
| SSC_BF(RCMR_STTDLY, 1)
|
||||||
| SSC_BF(RCMR_START, SSC_START_RISING_RF)
|
| SSC_BF(RCMR_START, SSC_START_RISING_RF)
|
||||||
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
|
||||||
|
@ -665,7 +734,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
| SSC_BF(RFMR_LOOP, 0)
|
| SSC_BF(RFMR_LOOP, 0)
|
||||||
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
| SSC_BF(RFMR_DATLEN, (bits - 1));
|
||||||
|
|
||||||
tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
|
tcmr = SSC_BF(TCMR_PERIOD, tcmr_period)
|
||||||
| SSC_BF(TCMR_STTDLY, 1)
|
| SSC_BF(TCMR_STTDLY, 1)
|
||||||
| SSC_BF(TCMR_START, SSC_START_RISING_RF)
|
| SSC_BF(TCMR_START, SSC_START_RISING_RF)
|
||||||
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
|
||||||
|
@ -760,7 +829,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set SSC clock mode register */
|
/* set SSC clock mode register */
|
||||||
ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
|
ssc_writel(ssc_p->ssc->regs, CMR, cmr_div);
|
||||||
|
|
||||||
/* set receive clock mode and format */
|
/* set receive clock mode and format */
|
||||||
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
|
ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
|
||||||
|
|
|
@ -113,6 +113,7 @@ struct atmel_ssc_info {
|
||||||
unsigned short cmr_div;
|
unsigned short cmr_div;
|
||||||
unsigned short tcmr_period;
|
unsigned short tcmr_period;
|
||||||
unsigned short rcmr_period;
|
unsigned short rcmr_period;
|
||||||
|
unsigned int forced_divider;
|
||||||
struct atmel_pcm_dma_params *dma_params[2];
|
struct atmel_pcm_dma_params *dma_params[2];
|
||||||
struct atmel_ssc_state ssc_state;
|
struct atmel_ssc_state ssc_state;
|
||||||
unsigned long mck_rate;
|
unsigned long mck_rate;
|
||||||
|
|
|
@ -53,7 +53,7 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct snd_soc_ops atmel_asoc_wm8904_ops = {
|
static const struct snd_soc_ops atmel_asoc_wm8904_ops = {
|
||||||
.hw_params = atmel_asoc_wm8904_hw_params,
|
.hw_params = atmel_asoc_wm8904_hw_params,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
472
sound/soc/atmel/tse850-pcm5142.c
Normal file
472
sound/soc/atmel/tse850-pcm5142.c
Normal file
|
@ -0,0 +1,472 @@
|
||||||
|
/*
|
||||||
|
* TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Axentia Technologies AB
|
||||||
|
*
|
||||||
|
* Author: Peter Rosin <peda@axentia.se>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* loop1 relays
|
||||||
|
* IN1 +---o +------------+ o---+ OUT1
|
||||||
|
* \ /
|
||||||
|
* + +
|
||||||
|
* | / |
|
||||||
|
* +--o +--. |
|
||||||
|
* | add | |
|
||||||
|
* | V |
|
||||||
|
* | .---. |
|
||||||
|
* DAC +----------->|Sum|---+
|
||||||
|
* | '---' |
|
||||||
|
* | |
|
||||||
|
* + +
|
||||||
|
*
|
||||||
|
* IN2 +---o--+------------+--o---+ OUT2
|
||||||
|
* loop2 relays
|
||||||
|
*
|
||||||
|
* The 'loop1' gpio pin controlls two relays, which are either in loop
|
||||||
|
* position, meaning that input and output are directly connected, or
|
||||||
|
* they are in mixer position, meaning that the signal is passed through
|
||||||
|
* the 'Sum' mixer. Similarly for 'loop2'.
|
||||||
|
*
|
||||||
|
* In the above, the 'loop1' relays are inactive, thus feeding IN1 to the
|
||||||
|
* mixer (if 'add' is active) and feeding the mixer output to OUT1. The
|
||||||
|
* 'loop2' relays are active, short-cutting the TSE-850 from channel 2.
|
||||||
|
* IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name
|
||||||
|
* of the (filtered) output from the PCM5142 codec.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
|
#include <sound/soc.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
|
||||||
|
#include "atmel_ssc_dai.h"
|
||||||
|
|
||||||
|
struct tse850_priv {
|
||||||
|
int ssc_id;
|
||||||
|
|
||||||
|
struct gpio_desc *add;
|
||||||
|
struct gpio_desc *loop1;
|
||||||
|
struct gpio_desc *loop2;
|
||||||
|
|
||||||
|
struct regulator *ana;
|
||||||
|
|
||||||
|
int add_cache;
|
||||||
|
int loop1_cache;
|
||||||
|
int loop2_cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tse850_get_mux1(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
ucontrol->value.enumerated.item[0] = tse850->loop1_cache;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tse850_put_mux1(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
|
||||||
|
unsigned int val = ucontrol->value.enumerated.item[0];
|
||||||
|
|
||||||
|
if (val >= e->items)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(tse850->loop1, val);
|
||||||
|
tse850->loop1_cache = val;
|
||||||
|
|
||||||
|
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tse850_get_mux2(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
ucontrol->value.enumerated.item[0] = tse850->loop2_cache;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tse850_put_mux2(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
|
||||||
|
unsigned int val = ucontrol->value.enumerated.item[0];
|
||||||
|
|
||||||
|
if (val >= e->items)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(tse850->loop2, val);
|
||||||
|
tse850->loop2_cache = val;
|
||||||
|
|
||||||
|
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tse850_get_mix(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
ucontrol->value.enumerated.item[0] = tse850->add_cache;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tse850_put_mix(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
int connect = !!ucontrol->value.integer.value[0];
|
||||||
|
|
||||||
|
if (tse850->add_cache == connect)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hmmm, this gpiod_set_value_cansleep call should probably happen
|
||||||
|
* inside snd_soc_dapm_mixer_update_power in the loop.
|
||||||
|
*/
|
||||||
|
gpiod_set_value_cansleep(tse850->add, connect);
|
||||||
|
tse850->add_cache = connect;
|
||||||
|
|
||||||
|
snd_soc_dapm_mixer_update_power(dapm, kctrl, connect, NULL);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tse850_get_ana(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regulator_get_voltage(tse850->ana);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map regulator output values like so:
|
||||||
|
* -11.5V to "Low" (enum 0)
|
||||||
|
* 11.5V-12.5V to "12V" (enum 1)
|
||||||
|
* 12.5V-13.5V to "13V" (enum 2)
|
||||||
|
* ...
|
||||||
|
* 18.5V-19.5V to "19V" (enum 8)
|
||||||
|
* 19.5V- to "20V" (enum 9)
|
||||||
|
*/
|
||||||
|
if (ret < 11000000)
|
||||||
|
ret = 11000000;
|
||||||
|
else if (ret > 20000000)
|
||||||
|
ret = 20000000;
|
||||||
|
ret -= 11000000;
|
||||||
|
ret = (ret + 500000) / 1000000;
|
||||||
|
|
||||||
|
ucontrol->value.enumerated.item[0] = ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tse850_put_ana(struct snd_kcontrol *kctrl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||||
|
struct snd_soc_card *card = dapm->card;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
|
||||||
|
unsigned int uV = ucontrol->value.enumerated.item[0];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (uV >= e->items)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map enum zero (Low) to 2 volts on the regulator, do this since
|
||||||
|
* the ana regulator is supplied by the system 12V voltage and
|
||||||
|
* requesting anything below the system voltage causes the system
|
||||||
|
* voltage to be passed through the regulator. Also, the ana
|
||||||
|
* regulator induces noise when requesting voltages near the
|
||||||
|
* system voltage. So, by mapping Low to 2V, that noise is
|
||||||
|
* eliminated when all that is needed is 12V (the system voltage).
|
||||||
|
*/
|
||||||
|
if (uV)
|
||||||
|
uV = 11000000 + (1000000 * uV);
|
||||||
|
else
|
||||||
|
uV = 2000000;
|
||||||
|
|
||||||
|
ret = regulator_set_voltage(tse850->ana, uV, uV);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const mux_text[] = { "Mixer", "Loop" };
|
||||||
|
|
||||||
|
static const struct soc_enum mux_enum =
|
||||||
|
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, mux_text);
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new mux1 =
|
||||||
|
SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1);
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new mux2 =
|
||||||
|
SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2);
|
||||||
|
|
||||||
|
#define TSE850_DAPM_SINGLE_EXT(xname, reg, shift, max, invert, xget, xput) \
|
||||||
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||||
|
.info = snd_soc_info_volsw, \
|
||||||
|
.get = xget, \
|
||||||
|
.put = xput, \
|
||||||
|
.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new mix[] = {
|
||||||
|
TSE850_DAPM_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0,
|
||||||
|
tse850_get_mix, tse850_put_mix),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const ana_text[] = {
|
||||||
|
"Low", "12V", "13V", "14V", "15V", "16V", "17V", "18V", "19V", "20V"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct soc_enum ana_enum =
|
||||||
|
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 9, ana_text);
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new out =
|
||||||
|
SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana);
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_widget tse850_dapm_widgets[] = {
|
||||||
|
SND_SOC_DAPM_LINE("OUT1", NULL),
|
||||||
|
SND_SOC_DAPM_LINE("OUT2", NULL),
|
||||||
|
SND_SOC_DAPM_LINE("IN1", NULL),
|
||||||
|
SND_SOC_DAPM_LINE("IN2", NULL),
|
||||||
|
SND_SOC_DAPM_INPUT("DAC"),
|
||||||
|
SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||||
|
SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
|
||||||
|
SOC_MIXER_ARRAY("MIX", SND_SOC_NOPM, 0, 0, mix),
|
||||||
|
SND_SOC_DAPM_MUX("MUX1", SND_SOC_NOPM, 0, 0, &mux1),
|
||||||
|
SND_SOC_DAPM_MUX("MUX2", SND_SOC_NOPM, 0, 0, &mux2),
|
||||||
|
SND_SOC_DAPM_OUT_DRV("OUT", SND_SOC_NOPM, 0, 0, &out, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These connections are not entirely correct, since both IN1 and IN2
|
||||||
|
* are always fed to MIX (if the "IN switch" is set so), i.e. without
|
||||||
|
* regard to the loop1 and loop2 relays that according to this only
|
||||||
|
* control MUX1 and MUX2 but in fact also control how the input signals
|
||||||
|
* are routed.
|
||||||
|
* But, 1) I don't know how to do it right, and 2) it doesn't seem to
|
||||||
|
* matter in practice since nothing is powered in those sections anyway.
|
||||||
|
*/
|
||||||
|
static const struct snd_soc_dapm_route tse850_intercon[] = {
|
||||||
|
{ "OUT1", NULL, "MUX1" },
|
||||||
|
{ "OUT2", NULL, "MUX2" },
|
||||||
|
|
||||||
|
{ "MUX1", "Loop", "IN1" },
|
||||||
|
{ "MUX1", "Mixer", "OUT" },
|
||||||
|
|
||||||
|
{ "MUX2", "Loop", "IN2" },
|
||||||
|
{ "MUX2", "Mixer", "OUT" },
|
||||||
|
|
||||||
|
{ "OUT", NULL, "MIX" },
|
||||||
|
|
||||||
|
{ "MIX", NULL, "DAC" },
|
||||||
|
{ "MIX", "IN Switch", "IN1" },
|
||||||
|
{ "MIX", "IN Switch", "IN2" },
|
||||||
|
|
||||||
|
/* connect board input to the codec left channel output pin */
|
||||||
|
{ "DAC", NULL, "OUTL" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_dai_link tse850_dailink = {
|
||||||
|
.name = "TSE-850",
|
||||||
|
.stream_name = "TSE-850-PCM",
|
||||||
|
.codec_dai_name = "pcm512x-hifi",
|
||||||
|
.dai_fmt = SND_SOC_DAIFMT_I2S
|
||||||
|
| SND_SOC_DAIFMT_NB_NF
|
||||||
|
| SND_SOC_DAIFMT_CBM_CFS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_card tse850_card = {
|
||||||
|
.name = "TSE-850-ASoC",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.dai_link = &tse850_dailink,
|
||||||
|
.num_links = 1,
|
||||||
|
.dapm_widgets = tse850_dapm_widgets,
|
||||||
|
.num_dapm_widgets = ARRAY_SIZE(tse850_dapm_widgets),
|
||||||
|
.dapm_routes = tse850_intercon,
|
||||||
|
.num_dapm_routes = ARRAY_SIZE(tse850_intercon),
|
||||||
|
.fully_routed = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tse850_dt_init(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct device_node *codec_np, *cpu_np;
|
||||||
|
struct snd_soc_card *card = &tse850_card;
|
||||||
|
struct snd_soc_dai_link *dailink = &tse850_dailink;
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
if (!np) {
|
||||||
|
dev_err(&pdev->dev, "only device tree supported\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_np = of_parse_phandle(np, "axentia,ssc-controller", 0);
|
||||||
|
if (!cpu_np) {
|
||||||
|
dev_err(&pdev->dev, "failed to get dai and pcm info\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
dailink->cpu_of_node = cpu_np;
|
||||||
|
dailink->platform_of_node = cpu_np;
|
||||||
|
tse850->ssc_id = of_alias_get_id(cpu_np, "ssc");
|
||||||
|
of_node_put(cpu_np);
|
||||||
|
|
||||||
|
codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
|
||||||
|
if (!codec_np) {
|
||||||
|
dev_err(&pdev->dev, "failed to get codec info\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
dailink->codec_of_node = codec_np;
|
||||||
|
of_node_put(codec_np);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tse850_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct snd_soc_card *card = &tse850_card;
|
||||||
|
struct device *dev = card->dev = &pdev->dev;
|
||||||
|
struct tse850_priv *tse850;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
tse850 = devm_kzalloc(dev, sizeof(*tse850), GFP_KERNEL);
|
||||||
|
if (!tse850)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
snd_soc_card_set_drvdata(card, tse850);
|
||||||
|
|
||||||
|
ret = tse850_dt_init(pdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to init dt info\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(tse850->add)) {
|
||||||
|
if (PTR_ERR(tse850->add) != -EPROBE_DEFER)
|
||||||
|
dev_err(dev, "failed to get 'add' gpio\n");
|
||||||
|
return PTR_ERR(tse850->add);
|
||||||
|
}
|
||||||
|
tse850->add_cache = 1;
|
||||||
|
|
||||||
|
tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(tse850->loop1)) {
|
||||||
|
if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER)
|
||||||
|
dev_err(dev, "failed to get 'loop1' gpio\n");
|
||||||
|
return PTR_ERR(tse850->loop1);
|
||||||
|
}
|
||||||
|
tse850->loop1_cache = 1;
|
||||||
|
|
||||||
|
tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(tse850->loop2)) {
|
||||||
|
if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER)
|
||||||
|
dev_err(dev, "failed to get 'loop2' gpio\n");
|
||||||
|
return PTR_ERR(tse850->loop2);
|
||||||
|
}
|
||||||
|
tse850->loop2_cache = 1;
|
||||||
|
|
||||||
|
tse850->ana = devm_regulator_get(dev, "axentia,ana");
|
||||||
|
if (IS_ERR(tse850->ana)) {
|
||||||
|
if (PTR_ERR(tse850->ana) != -EPROBE_DEFER)
|
||||||
|
dev_err(dev, "failed to get 'ana' regulator\n");
|
||||||
|
return PTR_ERR(tse850->ana);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regulator_enable(tse850->ana);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to enable the 'ana' regulator\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = atmel_ssc_set_audio(tse850->ssc_id);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(dev,
|
||||||
|
"failed to set SSC %d for audio\n", tse850->ssc_id);
|
||||||
|
goto err_disable_ana;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_soc_register_card(card);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "snd_soc_register_card failed\n");
|
||||||
|
goto err_put_audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_put_audio:
|
||||||
|
atmel_ssc_put_audio(tse850->ssc_id);
|
||||||
|
err_disable_ana:
|
||||||
|
regulator_disable(tse850->ana);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tse850_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||||
|
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
snd_soc_unregister_card(card);
|
||||||
|
atmel_ssc_put_audio(tse850->ssc_id);
|
||||||
|
regulator_disable(tse850->ana);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id tse850_dt_ids[] = {
|
||||||
|
{ .compatible = "axentia,tse850-pcm5142", },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, tse850_dt_ids);
|
||||||
|
|
||||||
|
static struct platform_driver tse850_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "axentia-tse850-pcm5142",
|
||||||
|
.of_match_table = of_match_ptr(tse850_dt_ids),
|
||||||
|
},
|
||||||
|
.probe = tse850_probe,
|
||||||
|
.remove = tse850_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(tse850_driver);
|
||||||
|
|
||||||
|
/* Module information */
|
||||||
|
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
|
||||||
|
MODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -11,6 +11,7 @@ config SND_BCM2835_SOC_I2S
|
||||||
config SND_SOC_CYGNUS
|
config SND_SOC_CYGNUS
|
||||||
tristate "SoC platform audio for Broadcom Cygnus chips"
|
tristate "SoC platform audio for Broadcom Cygnus chips"
|
||||||
depends on ARCH_BCM_CYGNUS || COMPILE_TEST
|
depends on ARCH_BCM_CYGNUS || COMPILE_TEST
|
||||||
|
depends on HAS_DMA
|
||||||
help
|
help
|
||||||
Say Y if you want to add support for ASoC audio on Broadcom
|
Say Y if you want to add support for ASoC audio on Broadcom
|
||||||
Cygnus chips (bcm958300, bcm958305, bcm911360)
|
Cygnus chips (bcm958300, bcm958305, bcm911360)
|
||||||
|
|
|
@ -2587,8 +2587,6 @@ static struct platform_driver ab8500_codec_platform_driver = {
|
||||||
},
|
},
|
||||||
.probe = ab8500_codec_driver_probe,
|
.probe = ab8500_codec_driver_probe,
|
||||||
.remove = ab8500_codec_driver_remove,
|
.remove = ab8500_codec_driver_remove,
|
||||||
.suspend = NULL,
|
|
||||||
.resume = NULL,
|
|
||||||
};
|
};
|
||||||
module_platform_driver(ab8500_codec_platform_driver);
|
module_platform_driver(ab8500_codec_platform_driver);
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,14 @@ int arizona_init_spk(struct snd_soc_codec *codec)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(arizona_init_spk);
|
||||||
|
|
||||||
|
int arizona_init_spk_irqs(struct arizona *arizona)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
|
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
|
||||||
"Thermal warning", arizona_thermal_warn,
|
"Thermal warning", arizona_thermal_warn,
|
||||||
arizona);
|
arizona);
|
||||||
|
@ -209,19 +217,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(arizona_init_spk);
|
EXPORT_SYMBOL_GPL(arizona_init_spk_irqs);
|
||||||
|
|
||||||
int arizona_free_spk(struct snd_soc_codec *codec)
|
int arizona_free_spk_irqs(struct arizona *arizona)
|
||||||
{
|
{
|
||||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
|
||||||
struct arizona *arizona = priv->arizona;
|
|
||||||
|
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona);
|
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona);
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona);
|
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(arizona_free_spk);
|
EXPORT_SYMBOL_GPL(arizona_free_spk_irqs);
|
||||||
|
|
||||||
static const struct snd_soc_dapm_route arizona_mono_routes[] = {
|
static const struct snd_soc_dapm_route arizona_mono_routes[] = {
|
||||||
{ "OUT1R", NULL, "OUT1L" },
|
{ "OUT1R", NULL, "OUT1L" },
|
||||||
|
@ -252,6 +257,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
|
||||||
int arizona_init_gpio(struct snd_soc_codec *codec)
|
int arizona_init_gpio(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
|
||||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->arizona;
|
struct arizona *arizona = priv->arizona;
|
||||||
int i;
|
int i;
|
||||||
|
@ -259,21 +265,24 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
|
||||||
switch (arizona->type) {
|
switch (arizona->type) {
|
||||||
case WM5110:
|
case WM5110:
|
||||||
case WM8280:
|
case WM8280:
|
||||||
snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
|
snd_soc_component_disable_pin(component,
|
||||||
|
"DRC2 Signal Activity");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
|
snd_soc_component_disable_pin(component, "DRC1 Signal Activity");
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
|
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
|
||||||
switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
|
switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
|
||||||
case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
|
case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
|
||||||
snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
|
snd_soc_component_enable_pin(component,
|
||||||
|
"DRC1 Signal Activity");
|
||||||
break;
|
break;
|
||||||
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
|
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
|
||||||
snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
|
snd_soc_component_enable_pin(component,
|
||||||
|
"DRC2 Signal Activity");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1233,6 +1242,46 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int arizona_clk_ev(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 arizona *arizona = dev_get_drvdata(codec->dev->parent);
|
||||||
|
unsigned int val;
|
||||||
|
int clk_idx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_read(arizona->regmap, w->reg, &val);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(codec->dev, "Failed to check clock source: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT;
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case ARIZONA_CLK_SRC_MCLK1:
|
||||||
|
clk_idx = ARIZONA_MCLK1;
|
||||||
|
break;
|
||||||
|
case ARIZONA_CLK_SRC_MCLK2:
|
||||||
|
clk_idx = ARIZONA_MCLK2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
|
return clk_prepare_enable(arizona->mclk[clk_idx]);
|
||||||
|
case SND_SOC_DAPM_POST_PMD:
|
||||||
|
clk_disable_unprepare(arizona->mclk[clk_idx]);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(arizona_clk_ev);
|
||||||
|
|
||||||
int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
||||||
int source, unsigned int freq, int dir)
|
int source, unsigned int freq, int dir)
|
||||||
{
|
{
|
||||||
|
@ -2242,6 +2291,42 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll, int base)
|
||||||
return reg & ARIZONA_FLL1_ENA;
|
return reg & ARIZONA_FLL1_ENA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena)
|
||||||
|
{
|
||||||
|
struct arizona *arizona = fll->arizona;
|
||||||
|
unsigned int val;
|
||||||
|
struct clk *clk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_read(arizona->regmap, base + 6, &val);
|
||||||
|
if (ret != 0) {
|
||||||
|
arizona_fll_err(fll, "Failed to read current source: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
val &= ARIZONA_FLL1_CLK_REF_SRC_MASK;
|
||||||
|
val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT;
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case ARIZONA_FLL_SRC_MCLK1:
|
||||||
|
clk = arizona->mclk[ARIZONA_MCLK1];
|
||||||
|
break;
|
||||||
|
case ARIZONA_FLL_SRC_MCLK2:
|
||||||
|
clk = arizona->mclk[ARIZONA_MCLK2];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ena) {
|
||||||
|
return clk_prepare_enable(clk);
|
||||||
|
} else {
|
||||||
|
clk_disable_unprepare(clk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int arizona_enable_fll(struct arizona_fll *fll)
|
static int arizona_enable_fll(struct arizona_fll *fll)
|
||||||
{
|
{
|
||||||
struct arizona *arizona = fll->arizona;
|
struct arizona *arizona = fll->arizona;
|
||||||
|
@ -2264,6 +2349,10 @@ static int arizona_enable_fll(struct arizona_fll *fll)
|
||||||
udelay(32);
|
udelay(32);
|
||||||
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
|
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
|
||||||
ARIZONA_FLL1_GAIN_MASK, 0);
|
ARIZONA_FLL1_GAIN_MASK, 0);
|
||||||
|
|
||||||
|
if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0)
|
||||||
|
arizona_set_fll_clks(fll, fll->base + 0x10, false);
|
||||||
|
arizona_set_fll_clks(fll, fll->base, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2318,10 +2407,13 @@ static int arizona_enable_fll(struct arizona_fll *fll)
|
||||||
if (!already_enabled)
|
if (!already_enabled)
|
||||||
pm_runtime_get_sync(arizona->dev);
|
pm_runtime_get_sync(arizona->dev);
|
||||||
|
|
||||||
if (use_sync)
|
if (use_sync) {
|
||||||
|
arizona_set_fll_clks(fll, fll->base + 0x10, true);
|
||||||
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
|
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
|
||||||
ARIZONA_FLL1_SYNC_ENA,
|
ARIZONA_FLL1_SYNC_ENA,
|
||||||
ARIZONA_FLL1_SYNC_ENA);
|
ARIZONA_FLL1_SYNC_ENA);
|
||||||
|
}
|
||||||
|
arizona_set_fll_clks(fll, fll->base, true);
|
||||||
regmap_update_bits_async(arizona->regmap, fll->base + 1,
|
regmap_update_bits_async(arizona->regmap, fll->base + 1,
|
||||||
ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
|
ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
|
||||||
|
|
||||||
|
@ -2354,20 +2446,25 @@ static int arizona_enable_fll(struct arizona_fll *fll)
|
||||||
static void arizona_disable_fll(struct arizona_fll *fll)
|
static void arizona_disable_fll(struct arizona_fll *fll)
|
||||||
{
|
{
|
||||||
struct arizona *arizona = fll->arizona;
|
struct arizona *arizona = fll->arizona;
|
||||||
bool change;
|
bool ref_change, sync_change;
|
||||||
|
|
||||||
regmap_update_bits_async(arizona->regmap, fll->base + 1,
|
regmap_update_bits_async(arizona->regmap, fll->base + 1,
|
||||||
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
|
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
|
||||||
regmap_update_bits_check(arizona->regmap, fll->base + 1,
|
regmap_update_bits_check(arizona->regmap, fll->base + 1,
|
||||||
ARIZONA_FLL1_ENA, 0, &change);
|
ARIZONA_FLL1_ENA, 0, &ref_change);
|
||||||
regmap_update_bits(arizona->regmap, fll->base + 0x11,
|
regmap_update_bits_check(arizona->regmap, fll->base + 0x11,
|
||||||
ARIZONA_FLL1_SYNC_ENA, 0);
|
ARIZONA_FLL1_SYNC_ENA, 0, &sync_change);
|
||||||
regmap_update_bits_async(arizona->regmap, fll->base + 1,
|
regmap_update_bits_async(arizona->regmap, fll->base + 1,
|
||||||
ARIZONA_FLL1_FREERUN, 0);
|
ARIZONA_FLL1_FREERUN, 0);
|
||||||
|
|
||||||
if (change)
|
if (sync_change)
|
||||||
|
arizona_set_fll_clks(fll, fll->base + 0x10, false);
|
||||||
|
|
||||||
|
if (ref_change) {
|
||||||
|
arizona_set_fll_clks(fll, fll->base, false);
|
||||||
pm_runtime_put_autosuspend(arizona->dev);
|
pm_runtime_put_autosuspend(arizona->dev);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
|
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
|
||||||
unsigned int Fref, unsigned int Fout)
|
unsigned int Fref, unsigned int Fout)
|
||||||
|
@ -2598,30 +2695,6 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
|
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
|
||||||
|
|
||||||
int arizona_register_notifier(struct snd_soc_codec *codec,
|
|
||||||
struct notifier_block *nb,
|
|
||||||
int (*notify)(struct notifier_block *nb,
|
|
||||||
unsigned long action, void *data))
|
|
||||||
{
|
|
||||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
|
||||||
struct arizona *arizona = priv->arizona;
|
|
||||||
|
|
||||||
nb->notifier_call = notify;
|
|
||||||
|
|
||||||
return blocking_notifier_chain_register(&arizona->notifier, nb);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(arizona_register_notifier);
|
|
||||||
|
|
||||||
int arizona_unregister_notifier(struct snd_soc_codec *codec,
|
|
||||||
struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
|
||||||
struct arizona *arizona = priv->arizona;
|
|
||||||
|
|
||||||
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
|
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
|
||||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#define _ASOC_ARIZONA_H
|
#define _ASOC_ARIZONA_H
|
||||||
|
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/mfd/arizona/core.h>
|
||||||
|
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
|
|
||||||
|
@ -66,7 +68,6 @@
|
||||||
/* Notifier events */
|
/* Notifier events */
|
||||||
#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
|
#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
|
||||||
|
|
||||||
struct arizona;
|
|
||||||
struct wm_adsp;
|
struct wm_adsp;
|
||||||
|
|
||||||
struct arizona_dai_priv {
|
struct arizona_dai_priv {
|
||||||
|
@ -255,26 +256,24 @@ extern const struct soc_enum arizona_output_anc_src[];
|
||||||
|
|
||||||
extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
|
extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
|
||||||
|
|
||||||
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
|
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
|
||||||
struct snd_kcontrol *kcontrol,
|
|
||||||
int event);
|
int event);
|
||||||
extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
|
int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
|
||||||
struct snd_kcontrol *kcontrol,
|
|
||||||
int event);
|
int event);
|
||||||
extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
|
int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
|
||||||
struct snd_kcontrol *kcontrol,
|
|
||||||
int event);
|
int event);
|
||||||
extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
|
||||||
struct snd_kcontrol *kcontrol,
|
|
||||||
int event);
|
int event);
|
||||||
|
|
||||||
extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
|
int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_value *ucontrol);
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
|
int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_value *ucontrol);
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
|
|
||||||
extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
|
||||||
int source, unsigned int freq, int dir);
|
int event);
|
||||||
|
int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source,
|
||||||
|
unsigned int freq, int dir);
|
||||||
|
|
||||||
extern const struct snd_soc_dai_ops arizona_dai_ops;
|
extern const struct snd_soc_dai_ops arizona_dai_ops;
|
||||||
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
|
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
|
||||||
|
@ -297,41 +296,57 @@ struct arizona_fll {
|
||||||
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
|
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
|
int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
|
||||||
extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
|
int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
|
||||||
extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
|
int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event);
|
struct snd_kcontrol *kcontrol, int event);
|
||||||
extern void arizona_init_dvfs(struct arizona_priv *priv);
|
void arizona_init_dvfs(struct arizona_priv *priv);
|
||||||
|
|
||||||
extern int arizona_init_fll(struct arizona *arizona, int id, int base,
|
int arizona_init_fll(struct arizona *arizona, int id, int base,
|
||||||
int lock_irq, int ok_irq, struct arizona_fll *fll);
|
int lock_irq, int ok_irq, struct arizona_fll *fll);
|
||||||
extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
|
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
|
||||||
unsigned int Fref, unsigned int Fout);
|
unsigned int Fref, unsigned int Fout);
|
||||||
extern int arizona_set_fll(struct arizona_fll *fll, int source,
|
int arizona_set_fll(struct arizona_fll *fll, int source,
|
||||||
unsigned int Fref, unsigned int Fout);
|
unsigned int Fref, unsigned int Fout);
|
||||||
|
|
||||||
extern int arizona_init_spk(struct snd_soc_codec *codec);
|
int arizona_init_spk(struct snd_soc_codec *codec);
|
||||||
extern int arizona_init_gpio(struct snd_soc_codec *codec);
|
int arizona_init_gpio(struct snd_soc_codec *codec);
|
||||||
extern int arizona_init_mono(struct snd_soc_codec *codec);
|
int arizona_init_mono(struct snd_soc_codec *codec);
|
||||||
extern int arizona_init_notifiers(struct snd_soc_codec *codec);
|
int arizona_init_notifiers(struct snd_soc_codec *codec);
|
||||||
|
|
||||||
extern int arizona_free_spk(struct snd_soc_codec *codec);
|
int arizona_init_spk_irqs(struct arizona *arizona);
|
||||||
|
int arizona_free_spk_irqs(struct arizona *arizona);
|
||||||
|
|
||||||
extern int arizona_init_dai(struct arizona_priv *priv, int dai);
|
int arizona_init_dai(struct arizona_priv *priv, int dai);
|
||||||
|
|
||||||
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
|
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
|
||||||
bool diff);
|
bool diff);
|
||||||
|
|
||||||
extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
|
bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
|
||||||
|
|
||||||
extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
|
const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
|
||||||
|
|
||||||
extern int arizona_register_notifier(struct snd_soc_codec *codec,
|
static inline int arizona_register_notifier(struct snd_soc_codec *codec,
|
||||||
struct notifier_block *nb,
|
struct notifier_block *nb,
|
||||||
int (*notify)(struct notifier_block *nb,
|
int (*notify)
|
||||||
unsigned long action,
|
(struct notifier_block *nb,
|
||||||
void *data));
|
unsigned long action, void *data))
|
||||||
extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
|
{
|
||||||
struct notifier_block *nb);
|
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
|
struct arizona *arizona = priv->arizona;
|
||||||
|
|
||||||
|
nb->notifier_call = notify;
|
||||||
|
|
||||||
|
return blocking_notifier_chain_register(&arizona->notifier, nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
|
||||||
|
struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
|
struct arizona *arizona = priv->arizona;
|
||||||
|
|
||||||
|
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -335,9 +335,11 @@ static const struct snd_kcontrol_new cs47l24_aec_loopback_mux =
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = {
|
static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
|
||||||
ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
||||||
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
||||||
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
||||||
|
@ -1064,7 +1066,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
|
||||||
static int cs47l24_open(struct snd_compr_stream *stream)
|
static int cs47l24_open(struct snd_compr_stream *stream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
||||||
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
|
struct cs47l24_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
struct arizona *arizona = priv->core.arizona;
|
||||||
int n_adsp;
|
int n_adsp;
|
||||||
|
|
||||||
|
@ -1113,8 +1115,8 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
|
||||||
static int cs47l24_codec_probe(struct snd_soc_codec *codec)
|
static int cs47l24_codec_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
|
||||||
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv->core.arizona->dapm = dapm;
|
priv->core.arizona->dapm = dapm;
|
||||||
|
@ -1124,14 +1126,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
|
||||||
arizona_init_mono(codec);
|
arizona_init_mono(codec);
|
||||||
arizona_init_notifiers(codec);
|
arizona_init_notifiers(codec);
|
||||||
|
|
||||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
|
||||||
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
|
|
||||||
priv);
|
|
||||||
if (ret != 0) {
|
|
||||||
dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
|
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_adsp2_codec_probe;
|
goto err_adsp2_codec_probe;
|
||||||
|
@ -1145,7 +1139,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_adsp2_codec_probe;
|
goto err_adsp2_codec_probe;
|
||||||
|
|
||||||
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
|
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1159,17 +1153,12 @@ err_adsp2_codec_probe:
|
||||||
static int cs47l24_codec_remove(struct snd_soc_codec *codec)
|
static int cs47l24_codec_remove(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
|
||||||
|
|
||||||
wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
|
wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
|
||||||
wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
|
wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
|
||||||
|
|
||||||
priv->core.arizona->dapm = NULL;
|
priv->core.arizona->dapm = NULL;
|
||||||
|
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
|
||||||
|
|
||||||
arizona_free_spk(codec);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1285,25 +1274,47 @@ static int cs47l24_probe(struct platform_device *pdev)
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
pm_runtime_idle(&pdev->dev);
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
|
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
||||||
|
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
|
||||||
|
cs47l24);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = arizona_init_spk_irqs(arizona);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_dsp_irq;
|
||||||
|
|
||||||
ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
|
ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
||||||
return ret;
|
goto err_spk_irqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
|
||||||
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
|
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
goto err_platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_platform:
|
||||||
|
snd_soc_unregister_platform(&pdev->dev);
|
||||||
|
err_spk_irqs:
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
err_dsp_irq:
|
||||||
|
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cs47l24_remove(struct platform_device *pdev)
|
static int cs47l24_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
|
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
|
||||||
|
struct arizona *arizona = cs47l24->core.arizona;
|
||||||
|
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
snd_soc_unregister_platform(&pdev->dev);
|
||||||
snd_soc_unregister_codec(&pdev->dev);
|
snd_soc_unregister_codec(&pdev->dev);
|
||||||
|
@ -1312,6 +1323,10 @@ static int cs47l24_remove(struct platform_device *pdev)
|
||||||
wm_adsp2_remove(&cs47l24->core.adsp[1]);
|
wm_adsp2_remove(&cs47l24->core.adsp[1]);
|
||||||
wm_adsp2_remove(&cs47l24->core.adsp[2]);
|
wm_adsp2_remove(&cs47l24->core.adsp[2]);
|
||||||
|
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
|
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -607,6 +607,9 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||||
break;
|
break;
|
||||||
case SND_SOC_DAPM_PRE_PMD:
|
case SND_SOC_DAPM_PRE_PMD:
|
||||||
break;
|
break;
|
||||||
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
|
case SND_SOC_DAPM_POST_PMD:
|
||||||
|
return arizona_clk_ev(w, kcontrol, event);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1077,9 +1080,11 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
|
||||||
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
|
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
||||||
0, wm5102_sysclk_ev,
|
0, wm5102_sysclk_ev,
|
||||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
||||||
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
||||||
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
||||||
|
@ -1903,7 +1908,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
|
||||||
static int wm5102_open(struct snd_compr_stream *stream)
|
static int wm5102_open(struct snd_compr_stream *stream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
||||||
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
|
struct wm5102_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
|
||||||
|
|
||||||
return wm_adsp_compr_open(&priv->core.adsp[0], stream);
|
return wm_adsp_compr_open(&priv->core.adsp[0], stream);
|
||||||
}
|
}
|
||||||
|
@ -1926,18 +1931,10 @@ static irqreturn_t wm5102_adsp2_irq(int irq, void *data)
|
||||||
static int wm5102_codec_probe(struct snd_soc_codec *codec)
|
static int wm5102_codec_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
|
||||||
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
|
||||||
"ADSP2 Compressed IRQ", wm5102_adsp2_irq,
|
|
||||||
priv);
|
|
||||||
if (ret != 0) {
|
|
||||||
dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
|
ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1949,8 +1946,9 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
|
||||||
|
|
||||||
arizona_init_spk(codec);
|
arizona_init_spk(codec);
|
||||||
arizona_init_gpio(codec);
|
arizona_init_gpio(codec);
|
||||||
|
arizona_init_notifiers(codec);
|
||||||
|
|
||||||
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
|
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||||
|
|
||||||
priv->core.arizona->dapm = dapm;
|
priv->core.arizona->dapm = dapm;
|
||||||
|
|
||||||
|
@ -1965,16 +1963,11 @@ err_adsp2_codec_probe:
|
||||||
static int wm5102_codec_remove(struct snd_soc_codec *codec)
|
static int wm5102_codec_remove(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
|
||||||
|
|
||||||
wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
|
wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
|
||||||
|
|
||||||
priv->core.arizona->dapm = NULL;
|
priv->core.arizona->dapm = NULL;
|
||||||
|
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
|
||||||
|
|
||||||
arizona_free_spk(codec);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2092,25 +2085,47 @@ static int wm5102_probe(struct platform_device *pdev)
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
pm_runtime_idle(&pdev->dev);
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
|
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
||||||
|
"ADSP2 Compressed IRQ", wm5102_adsp2_irq,
|
||||||
|
wm5102);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = arizona_init_spk_irqs(arizona);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_dsp_irq;
|
||||||
|
|
||||||
ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform);
|
ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
||||||
return ret;
|
goto err_spk_irqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
|
||||||
wm5102_dai, ARRAY_SIZE(wm5102_dai));
|
wm5102_dai, ARRAY_SIZE(wm5102_dai));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
goto err_platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_platform:
|
||||||
|
snd_soc_unregister_platform(&pdev->dev);
|
||||||
|
err_spk_irqs:
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
err_dsp_irq:
|
||||||
|
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm5102_remove(struct platform_device *pdev)
|
static int wm5102_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
|
struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
|
||||||
|
struct arizona *arizona = wm5102->core.arizona;
|
||||||
|
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
snd_soc_unregister_platform(&pdev->dev);
|
||||||
snd_soc_unregister_codec(&pdev->dev);
|
snd_soc_unregister_codec(&pdev->dev);
|
||||||
|
@ -2118,6 +2133,10 @@ static int wm5102_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
wm_adsp2_remove(&wm5102->core.adsp[0]);
|
wm_adsp2_remove(&wm5102->core.adsp[0]);
|
||||||
|
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
|
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,7 +183,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||||
regmap_write_async(regmap, patch[i].reg,
|
regmap_write_async(regmap, patch[i].reg,
|
||||||
patch[i].def);
|
patch[i].def);
|
||||||
break;
|
break;
|
||||||
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
|
case SND_SOC_DAPM_POST_PMD:
|
||||||
|
return arizona_clk_ev(w, kcontrol, event);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1073,9 +1075,11 @@ static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
|
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
||||||
0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
|
0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU |
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
||||||
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
||||||
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
||||||
|
@ -2220,7 +2224,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
|
||||||
static int wm5110_open(struct snd_compr_stream *stream)
|
static int wm5110_open(struct snd_compr_stream *stream)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
||||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
|
struct wm5110_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
struct arizona *arizona = priv->core.arizona;
|
||||||
int n_adsp;
|
int n_adsp;
|
||||||
|
|
||||||
|
@ -2269,8 +2273,8 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
|
||||||
static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
|
||||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
priv->core.arizona->dapm = dapm;
|
priv->core.arizona->dapm = dapm;
|
||||||
|
@ -2280,14 +2284,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
||||||
arizona_init_mono(codec);
|
arizona_init_mono(codec);
|
||||||
arizona_init_notifiers(codec);
|
arizona_init_notifiers(codec);
|
||||||
|
|
||||||
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
|
||||||
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
|
|
||||||
priv);
|
|
||||||
if (ret != 0) {
|
|
||||||
dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
|
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
|
||||||
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
|
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -2300,7 +2296,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_adsp2_codec_probe;
|
goto err_adsp2_codec_probe;
|
||||||
|
|
||||||
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
|
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2308,15 +2304,12 @@ err_adsp2_codec_probe:
|
||||||
for (--i; i >= 0; --i)
|
for (--i; i >= 0; --i)
|
||||||
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
|
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
|
||||||
|
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm5110_codec_remove(struct snd_soc_codec *codec)
|
static int wm5110_codec_remove(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct arizona *arizona = priv->core.arizona;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < WM5110_NUM_ADSP; ++i)
|
for (i = 0; i < WM5110_NUM_ADSP; ++i)
|
||||||
|
@ -2324,10 +2317,6 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec)
|
||||||
|
|
||||||
priv->core.arizona->dapm = NULL;
|
priv->core.arizona->dapm = NULL;
|
||||||
|
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
|
||||||
|
|
||||||
arizona_free_spk(codec);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2449,25 +2438,47 @@ static int wm5110_probe(struct platform_device *pdev)
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
pm_runtime_idle(&pdev->dev);
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
|
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
|
||||||
|
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
|
||||||
|
wm5110);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = arizona_init_spk_irqs(arizona);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_dsp_irq;
|
||||||
|
|
||||||
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
|
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
||||||
return ret;
|
goto err_spk_irqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
|
||||||
wm5110_dai, ARRAY_SIZE(wm5110_dai));
|
wm5110_dai, ARRAY_SIZE(wm5110_dai));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
goto err_platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_platform:
|
||||||
|
snd_soc_unregister_platform(&pdev->dev);
|
||||||
|
err_spk_irqs:
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
err_dsp_irq:
|
||||||
|
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm5110_remove(struct platform_device *pdev)
|
static int wm5110_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
|
struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
|
||||||
|
struct arizona *arizona = wm5110->core.arizona;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
snd_soc_unregister_platform(&pdev->dev);
|
||||||
|
@ -2477,6 +2488,10 @@ static int wm5110_remove(struct platform_device *pdev)
|
||||||
for (i = 0; i < WM5110_NUM_ADSP; i++)
|
for (i = 0; i < WM5110_NUM_ADSP; i++)
|
||||||
wm_adsp2_remove(&wm5110->core.adsp[i]);
|
wm_adsp2_remove(&wm5110->core.adsp[i]);
|
||||||
|
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
|
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,9 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||||
break;
|
break;
|
||||||
case SND_SOC_DAPM_PRE_PMD:
|
case SND_SOC_DAPM_PRE_PMD:
|
||||||
break;
|
break;
|
||||||
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
|
case SND_SOC_DAPM_POST_PMD:
|
||||||
|
return arizona_clk_ev(w, kcontrol, event);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -408,9 +411,11 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
|
||||||
static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
|
static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
||||||
0, wm8997_sysclk_ev,
|
0, wm8997_sysclk_ev,
|
||||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
||||||
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
||||||
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
||||||
|
@ -1055,11 +1060,13 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
|
||||||
static int wm8997_codec_probe(struct snd_soc_codec *codec)
|
static int wm8997_codec_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
|
||||||
struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
arizona_init_spk(codec);
|
arizona_init_spk(codec);
|
||||||
|
arizona_init_notifiers(codec);
|
||||||
|
|
||||||
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
|
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||||
|
|
||||||
priv->core.arizona->dapm = dapm;
|
priv->core.arizona->dapm = dapm;
|
||||||
|
|
||||||
|
@ -1072,8 +1079,6 @@ static int wm8997_codec_remove(struct snd_soc_codec *codec)
|
||||||
|
|
||||||
priv->core.arizona->dapm = NULL;
|
priv->core.arizona->dapm = NULL;
|
||||||
|
|
||||||
arizona_free_spk(codec);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1119,7 +1124,7 @@ static int wm8997_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct wm8997_priv *wm8997;
|
struct wm8997_priv *wm8997;
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
|
wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -1159,15 +1164,33 @@ static int wm8997_probe(struct platform_device *pdev)
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
pm_runtime_idle(&pdev->dev);
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
|
ret = arizona_init_spk_irqs(arizona);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
|
||||||
wm8997_dai, ARRAY_SIZE(wm8997_dai));
|
wm8997_dai, ARRAY_SIZE(wm8997_dai));
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||||
|
goto err_spk_irqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_spk_irqs:
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm8997_remove(struct platform_device *pdev)
|
static int wm8997_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct wm8997_priv *wm8997 = platform_get_drvdata(pdev);
|
||||||
|
struct arizona *arizona = wm8997->core.arizona;
|
||||||
|
|
||||||
snd_soc_unregister_codec(&pdev->dev);
|
snd_soc_unregister_codec(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -541,9 +541,11 @@ static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
|
static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
|
||||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
|
||||||
ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
|
||||||
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
|
||||||
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
|
||||||
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
|
||||||
|
@ -1318,13 +1320,15 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
|
struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
|
||||||
|
|
||||||
priv->core.arizona->dapm = dapm;
|
priv->core.arizona->dapm = dapm;
|
||||||
|
|
||||||
arizona_init_spk(codec);
|
arizona_init_spk(codec);
|
||||||
arizona_init_gpio(codec);
|
arizona_init_gpio(codec);
|
||||||
|
arizona_init_notifiers(codec);
|
||||||
|
|
||||||
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
|
snd_soc_component_disable_pin(component, "HAPTICS");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1335,8 +1339,6 @@ static int wm8998_codec_remove(struct snd_soc_codec *codec)
|
||||||
|
|
||||||
priv->core.arizona->dapm = NULL;
|
priv->core.arizona->dapm = NULL;
|
||||||
|
|
||||||
arizona_free_spk(codec);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,7 +1387,7 @@ static int wm8998_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct wm8998_priv *wm8998;
|
struct wm8998_priv *wm8998;
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
|
wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -1417,15 +1419,35 @@ static int wm8998_probe(struct platform_device *pdev)
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
pm_runtime_idle(&pdev->dev);
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
|
ret = arizona_init_spk_irqs(arizona);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
|
||||||
wm8998_dai, ARRAY_SIZE(wm8998_dai));
|
wm8998_dai, ARRAY_SIZE(wm8998_dai));
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||||
|
goto err_spk_irqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_spk_irqs:
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm8998_remove(struct platform_device *pdev)
|
static int wm8998_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct wm8998_priv *wm8998 = platform_get_drvdata(pdev);
|
||||||
|
struct arizona *arizona = wm8998->core.arizona;
|
||||||
|
|
||||||
snd_soc_unregister_codec(&pdev->dev);
|
snd_soc_unregister_codec(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
arizona_free_spk_irqs(arizona);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,16 @@
|
||||||
|
|
||||||
#define ADSP_MAX_STD_CTRL_SIZE 512
|
#define ADSP_MAX_STD_CTRL_SIZE 512
|
||||||
|
|
||||||
|
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
|
||||||
|
#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
|
||||||
|
#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
|
||||||
|
#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event control messages
|
||||||
|
*/
|
||||||
|
#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
|
||||||
|
|
||||||
struct wm_adsp_buf {
|
struct wm_adsp_buf {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
void *buf;
|
void *buf;
|
||||||
|
@ -177,7 +187,7 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
|
||||||
|
|
||||||
buf->buf = vmalloc(len);
|
buf->buf = vmalloc(len);
|
||||||
if (!buf->buf) {
|
if (!buf->buf) {
|
||||||
vfree(buf);
|
kfree(buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memcpy(buf->buf, src, len);
|
memcpy(buf->buf, src, len);
|
||||||
|
@ -441,11 +451,29 @@ struct wm_coeff_ctl {
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned int set:1;
|
unsigned int set:1;
|
||||||
struct snd_kcontrol *kcontrol;
|
|
||||||
struct soc_bytes_ext bytes_ext;
|
struct soc_bytes_ext bytes_ext;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
unsigned int type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *wm_adsp_mem_region_name(unsigned int type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case WMFW_ADSP1_PM:
|
||||||
|
return "PM";
|
||||||
|
case WMFW_ADSP1_DM:
|
||||||
|
return "DM";
|
||||||
|
case WMFW_ADSP2_XM:
|
||||||
|
return "XM";
|
||||||
|
case WMFW_ADSP2_YM:
|
||||||
|
return "YM";
|
||||||
|
case WMFW_ADSP1_ZM:
|
||||||
|
return "ZM";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
|
static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
|
||||||
{
|
{
|
||||||
|
@ -727,27 +755,11 @@ 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);
|
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wm_coeff_info(struct snd_kcontrol *kctl,
|
static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
|
||||||
struct snd_ctl_elem_info *uinfo)
|
|
||||||
{
|
{
|
||||||
struct soc_bytes_ext *bytes_ext =
|
const struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
|
||||||
(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;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
|
|
||||||
const void *buf, size_t len)
|
|
||||||
{
|
|
||||||
struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
|
|
||||||
const struct wm_adsp_region *mem;
|
|
||||||
struct wm_adsp *dsp = ctl->dsp;
|
struct wm_adsp *dsp = ctl->dsp;
|
||||||
void *scratch;
|
const struct wm_adsp_region *mem;
|
||||||
int ret;
|
|
||||||
unsigned int reg;
|
|
||||||
|
|
||||||
mem = wm_adsp_find_region(dsp, alg_region->type);
|
mem = wm_adsp_find_region(dsp, alg_region->type);
|
||||||
if (!mem) {
|
if (!mem) {
|
||||||
|
@ -756,8 +768,106 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = ctl->alg_region.base + ctl->offset;
|
*reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
|
||||||
reg = wm_adsp_region_to_reg(mem, reg);
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm_coeff_info(struct snd_kcontrol *kctl,
|
||||||
|
struct snd_ctl_elem_info *uinfo)
|
||||||
|
{
|
||||||
|
struct soc_bytes_ext *bytes_ext =
|
||||||
|
(struct soc_bytes_ext *)kctl->private_value;
|
||||||
|
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
|
||||||
|
|
||||||
|
switch (ctl->type) {
|
||||||
|
case WMFW_CTL_TYPE_ACKED:
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||||
|
uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
|
||||||
|
uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
|
||||||
|
uinfo->value.integer.step = 1;
|
||||||
|
uinfo->count = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||||
|
uinfo->count = ctl->len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
|
||||||
|
unsigned int event_id)
|
||||||
|
{
|
||||||
|
struct wm_adsp *dsp = ctl->dsp;
|
||||||
|
u32 val = cpu_to_be32(event_id);
|
||||||
|
unsigned int reg;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
ret = wm_coeff_base_reg(ctl, ®);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
|
||||||
|
event_id, ctl->alg_region.alg,
|
||||||
|
wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
|
||||||
|
|
||||||
|
ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
|
||||||
|
if (ret) {
|
||||||
|
adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Poll for ack, we initially poll at ~1ms intervals for firmwares
|
||||||
|
* that respond quickly, then go to ~10ms polls. A firmware is unlikely
|
||||||
|
* to ack instantly so we do the first 1ms delay before reading the
|
||||||
|
* control to avoid a pointless bus transaction
|
||||||
|
*/
|
||||||
|
for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
|
||||||
|
switch (i) {
|
||||||
|
case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usleep_range(10000, 20000);
|
||||||
|
i += 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
|
||||||
|
if (ret) {
|
||||||
|
adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val == 0) {
|
||||||
|
adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
|
||||||
|
reg, ctl->alg_region.alg,
|
||||||
|
wm_adsp_mem_region_name(ctl->alg_region.type),
|
||||||
|
ctl->offset);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
|
||||||
|
const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct wm_adsp *dsp = ctl->dsp;
|
||||||
|
void *scratch;
|
||||||
|
int ret;
|
||||||
|
unsigned int reg;
|
||||||
|
|
||||||
|
ret = wm_coeff_base_reg(ctl, ®);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
|
scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
|
||||||
if (!scratch)
|
if (!scratch)
|
||||||
|
@ -823,25 +933,41 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct soc_bytes_ext *bytes_ext =
|
||||||
|
(struct soc_bytes_ext *)kctl->private_value;
|
||||||
|
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
|
||||||
|
unsigned int val = ucontrol->value.integer.value[0];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (val == 0)
|
||||||
|
return 0; /* 0 means no event */
|
||||||
|
|
||||||
|
mutex_lock(&ctl->dsp->pwr_lock);
|
||||||
|
|
||||||
|
if (ctl->enabled)
|
||||||
|
ret = wm_coeff_write_acked_control(ctl, val);
|
||||||
|
else
|
||||||
|
ret = -EPERM;
|
||||||
|
|
||||||
|
mutex_unlock(&ctl->dsp->pwr_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
|
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
|
||||||
void *buf, size_t len)
|
void *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
|
|
||||||
const struct wm_adsp_region *mem;
|
|
||||||
struct wm_adsp *dsp = ctl->dsp;
|
struct wm_adsp *dsp = ctl->dsp;
|
||||||
void *scratch;
|
void *scratch;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int reg;
|
unsigned int reg;
|
||||||
|
|
||||||
mem = wm_adsp_find_region(dsp, alg_region->type);
|
ret = wm_coeff_base_reg(ctl, ®);
|
||||||
if (!mem) {
|
if (ret)
|
||||||
adsp_err(dsp, "No base for region %x\n",
|
return ret;
|
||||||
alg_region->type);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg = ctl->alg_region.base + ctl->offset;
|
|
||||||
reg = wm_adsp_region_to_reg(mem, reg);
|
|
||||||
|
|
||||||
scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
|
scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
|
||||||
if (!scratch)
|
if (!scratch)
|
||||||
|
@ -918,6 +1044,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Although it's not useful to read an acked control, we must satisfy
|
||||||
|
* user-side assumptions that all controls are readable and that a
|
||||||
|
* write of the same value should be filtered out (it's valid to send
|
||||||
|
* the same event number again to the firmware). We therefore return 0,
|
||||||
|
* meaning "no event" so valid event numbers will always be a change
|
||||||
|
*/
|
||||||
|
ucontrol->value.integer.value[0] = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct wmfw_ctl_work {
|
struct wmfw_ctl_work {
|
||||||
struct wm_adsp *dsp;
|
struct wm_adsp *dsp;
|
||||||
struct wm_coeff_ctl *ctl;
|
struct wm_coeff_ctl *ctl;
|
||||||
|
@ -967,30 +1108,35 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
|
||||||
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
|
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
|
||||||
if (!kcontrol)
|
if (!kcontrol)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
|
||||||
|
|
||||||
kcontrol->name = ctl->name;
|
kcontrol->name = ctl->name;
|
||||||
kcontrol->info = wm_coeff_info;
|
kcontrol->info = wm_coeff_info;
|
||||||
kcontrol->get = wm_coeff_get;
|
|
||||||
kcontrol->put = wm_coeff_put;
|
|
||||||
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||||
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
|
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
|
||||||
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
|
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
|
||||||
|
kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
|
||||||
|
|
||||||
|
switch (ctl->type) {
|
||||||
|
case WMFW_CTL_TYPE_ACKED:
|
||||||
|
kcontrol->get = wm_coeff_get_acked;
|
||||||
|
kcontrol->put = wm_coeff_put_acked;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
kcontrol->get = wm_coeff_get;
|
||||||
|
kcontrol->put = wm_coeff_put;
|
||||||
|
|
||||||
ctl->bytes_ext.max = ctl->len;
|
ctl->bytes_ext.max = ctl->len;
|
||||||
ctl->bytes_ext.get = wm_coeff_tlv_get;
|
ctl->bytes_ext.get = wm_coeff_tlv_get;
|
||||||
ctl->bytes_ext.put = wm_coeff_tlv_put;
|
ctl->bytes_ext.put = wm_coeff_tlv_put;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
|
ret = snd_soc_add_codec_controls(dsp->codec, kcontrol, 1);
|
||||||
|
|
||||||
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_kcontrol;
|
goto err_kcontrol;
|
||||||
|
|
||||||
kfree(kcontrol);
|
kfree(kcontrol);
|
||||||
|
|
||||||
ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_kcontrol:
|
err_kcontrol:
|
||||||
|
@ -1035,6 +1181,27 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
|
||||||
|
unsigned int event)
|
||||||
|
{
|
||||||
|
struct wm_coeff_ctl *ctl;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
list_for_each_entry(ctl, &dsp->ctl_list, list) {
|
||||||
|
if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ctl->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = wm_coeff_write_acked_control(ctl, event);
|
||||||
|
if (ret)
|
||||||
|
adsp_warn(dsp,
|
||||||
|
"Failed to send 0x%x event to alg 0x%x (%d)\n",
|
||||||
|
event, ctl->alg_region.alg, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void wm_adsp_ctl_work(struct work_struct *work)
|
static void wm_adsp_ctl_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct wmfw_ctl_work *ctl_work = container_of(work,
|
struct wmfw_ctl_work *ctl_work = container_of(work,
|
||||||
|
@ -1056,34 +1223,16 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
|
||||||
const struct wm_adsp_alg_region *alg_region,
|
const struct wm_adsp_alg_region *alg_region,
|
||||||
unsigned int offset, unsigned int len,
|
unsigned int offset, unsigned int len,
|
||||||
const char *subname, unsigned int subname_len,
|
const char *subname, unsigned int subname_len,
|
||||||
unsigned int flags)
|
unsigned int flags, unsigned int type)
|
||||||
{
|
{
|
||||||
struct wm_coeff_ctl *ctl;
|
struct wm_coeff_ctl *ctl;
|
||||||
struct wmfw_ctl_work *ctl_work;
|
struct wmfw_ctl_work *ctl_work;
|
||||||
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||||
char *region_name;
|
const char *region_name;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (flags & WMFW_CTL_FLAG_SYS)
|
region_name = wm_adsp_mem_region_name(alg_region->type);
|
||||||
return 0;
|
if (!region_name) {
|
||||||
|
|
||||||
switch (alg_region->type) {
|
|
||||||
case WMFW_ADSP1_PM:
|
|
||||||
region_name = "PM";
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP1_DM:
|
|
||||||
region_name = "DM";
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP2_XM:
|
|
||||||
region_name = "XM";
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP2_YM:
|
|
||||||
region_name = "YM";
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP1_ZM:
|
|
||||||
region_name = "ZM";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
|
adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1139,6 +1288,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
|
||||||
ctl->dsp = dsp;
|
ctl->dsp = dsp;
|
||||||
|
|
||||||
ctl->flags = flags;
|
ctl->flags = flags;
|
||||||
|
ctl->type = type;
|
||||||
ctl->offset = offset;
|
ctl->offset = offset;
|
||||||
ctl->len = len;
|
ctl->len = len;
|
||||||
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
|
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
|
||||||
|
@ -1149,6 +1299,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
|
||||||
|
|
||||||
list_add(&ctl->list, &dsp->ctl_list);
|
list_add(&ctl->list, &dsp->ctl_list);
|
||||||
|
|
||||||
|
if (flags & WMFW_CTL_FLAG_SYS)
|
||||||
|
return 0;
|
||||||
|
|
||||||
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
|
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
|
||||||
if (!ctl_work) {
|
if (!ctl_work) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
@ -1308,6 +1461,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
|
||||||
adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
|
adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
|
||||||
|
const struct wm_coeff_parsed_coeff *coeff_blk,
|
||||||
|
unsigned int f_required,
|
||||||
|
unsigned int f_illegal)
|
||||||
|
{
|
||||||
|
if ((coeff_blk->flags & f_illegal) ||
|
||||||
|
((coeff_blk->flags & f_required) != f_required)) {
|
||||||
|
adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
|
||||||
|
coeff_blk->flags, coeff_blk->ctl_type);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
||||||
const struct wmfw_region *region)
|
const struct wmfw_region *region)
|
||||||
{
|
{
|
||||||
|
@ -1324,6 +1492,28 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
||||||
switch (coeff_blk.ctl_type) {
|
switch (coeff_blk.ctl_type) {
|
||||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||||
break;
|
break;
|
||||||
|
case WMFW_CTL_TYPE_ACKED:
|
||||||
|
if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
|
||||||
|
continue; /* ignore */
|
||||||
|
|
||||||
|
ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
|
||||||
|
WMFW_CTL_FLAG_VOLATILE |
|
||||||
|
WMFW_CTL_FLAG_WRITEABLE |
|
||||||
|
WMFW_CTL_FLAG_READABLE,
|
||||||
|
0);
|
||||||
|
if (ret)
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
case WMFW_CTL_TYPE_HOSTEVENT:
|
||||||
|
ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
|
||||||
|
WMFW_CTL_FLAG_SYS |
|
||||||
|
WMFW_CTL_FLAG_VOLATILE |
|
||||||
|
WMFW_CTL_FLAG_WRITEABLE |
|
||||||
|
WMFW_CTL_FLAG_READABLE,
|
||||||
|
0);
|
||||||
|
if (ret)
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
adsp_err(dsp, "Unknown control type: %d\n",
|
adsp_err(dsp, "Unknown control type: %d\n",
|
||||||
coeff_blk.ctl_type);
|
coeff_blk.ctl_type);
|
||||||
|
@ -1338,7 +1528,8 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
||||||
coeff_blk.len,
|
coeff_blk.len,
|
||||||
coeff_blk.name,
|
coeff_blk.name,
|
||||||
coeff_blk.name_len,
|
coeff_blk.name_len,
|
||||||
coeff_blk.flags);
|
coeff_blk.flags,
|
||||||
|
coeff_blk.ctl_type);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
adsp_err(dsp, "Failed to create control: %.*s, %d\n",
|
adsp_err(dsp, "Failed to create control: %.*s, %d\n",
|
||||||
coeff_blk.name_len, coeff_blk.name, ret);
|
coeff_blk.name_len, coeff_blk.name, ret);
|
||||||
|
@ -1491,23 +1682,11 @@ static int wm_adsp_load(struct wm_adsp *dsp)
|
||||||
reg = offset;
|
reg = offset;
|
||||||
break;
|
break;
|
||||||
case WMFW_ADSP1_PM:
|
case WMFW_ADSP1_PM:
|
||||||
region_name = "PM";
|
|
||||||
reg = wm_adsp_region_to_reg(mem, offset);
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP1_DM:
|
case WMFW_ADSP1_DM:
|
||||||
region_name = "DM";
|
|
||||||
reg = wm_adsp_region_to_reg(mem, offset);
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP2_XM:
|
case WMFW_ADSP2_XM:
|
||||||
region_name = "XM";
|
|
||||||
reg = wm_adsp_region_to_reg(mem, offset);
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP2_YM:
|
case WMFW_ADSP2_YM:
|
||||||
region_name = "YM";
|
|
||||||
reg = wm_adsp_region_to_reg(mem, offset);
|
|
||||||
break;
|
|
||||||
case WMFW_ADSP1_ZM:
|
case WMFW_ADSP1_ZM:
|
||||||
region_name = "ZM";
|
region_name = wm_adsp_mem_region_name(type);
|
||||||
reg = wm_adsp_region_to_reg(mem, offset);
|
reg = wm_adsp_region_to_reg(mem, offset);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1750,7 +1929,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
|
||||||
len -= be32_to_cpu(adsp1_alg[i].dm);
|
len -= be32_to_cpu(adsp1_alg[i].dm);
|
||||||
len *= 4;
|
len *= 4;
|
||||||
wm_adsp_create_control(dsp, alg_region, 0,
|
wm_adsp_create_control(dsp, alg_region, 0,
|
||||||
len, NULL, 0, 0);
|
len, NULL, 0, 0,
|
||||||
|
SNDRV_CTL_ELEM_TYPE_BYTES);
|
||||||
} else {
|
} else {
|
||||||
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
|
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
|
||||||
be32_to_cpu(adsp1_alg[i].alg.id));
|
be32_to_cpu(adsp1_alg[i].alg.id));
|
||||||
|
@ -1770,7 +1950,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
|
||||||
len -= be32_to_cpu(adsp1_alg[i].zm);
|
len -= be32_to_cpu(adsp1_alg[i].zm);
|
||||||
len *= 4;
|
len *= 4;
|
||||||
wm_adsp_create_control(dsp, alg_region, 0,
|
wm_adsp_create_control(dsp, alg_region, 0,
|
||||||
len, NULL, 0, 0);
|
len, NULL, 0, 0,
|
||||||
|
SNDRV_CTL_ELEM_TYPE_BYTES);
|
||||||
} else {
|
} else {
|
||||||
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
|
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
|
||||||
be32_to_cpu(adsp1_alg[i].alg.id));
|
be32_to_cpu(adsp1_alg[i].alg.id));
|
||||||
|
@ -1861,7 +2042,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
|
||||||
len -= be32_to_cpu(adsp2_alg[i].xm);
|
len -= be32_to_cpu(adsp2_alg[i].xm);
|
||||||
len *= 4;
|
len *= 4;
|
||||||
wm_adsp_create_control(dsp, alg_region, 0,
|
wm_adsp_create_control(dsp, alg_region, 0,
|
||||||
len, NULL, 0, 0);
|
len, NULL, 0, 0,
|
||||||
|
SNDRV_CTL_ELEM_TYPE_BYTES);
|
||||||
} else {
|
} else {
|
||||||
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
|
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
|
||||||
be32_to_cpu(adsp2_alg[i].alg.id));
|
be32_to_cpu(adsp2_alg[i].alg.id));
|
||||||
|
@ -1881,7 +2063,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
|
||||||
len -= be32_to_cpu(adsp2_alg[i].ym);
|
len -= be32_to_cpu(adsp2_alg[i].ym);
|
||||||
len *= 4;
|
len *= 4;
|
||||||
wm_adsp_create_control(dsp, alg_region, 0,
|
wm_adsp_create_control(dsp, alg_region, 0,
|
||||||
len, NULL, 0, 0);
|
len, NULL, 0, 0,
|
||||||
|
SNDRV_CTL_ELEM_TYPE_BYTES);
|
||||||
} else {
|
} else {
|
||||||
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
|
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
|
||||||
be32_to_cpu(adsp2_alg[i].alg.id));
|
be32_to_cpu(adsp2_alg[i].alg.id));
|
||||||
|
@ -1901,7 +2084,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
|
||||||
len -= be32_to_cpu(adsp2_alg[i].zm);
|
len -= be32_to_cpu(adsp2_alg[i].zm);
|
||||||
len *= 4;
|
len *= 4;
|
||||||
wm_adsp_create_control(dsp, alg_region, 0,
|
wm_adsp_create_control(dsp, alg_region, 0,
|
||||||
len, NULL, 0, 0);
|
len, NULL, 0, 0,
|
||||||
|
SNDRV_CTL_ELEM_TYPE_BYTES);
|
||||||
} else {
|
} else {
|
||||||
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
|
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
|
||||||
be32_to_cpu(adsp2_alg[i].alg.id));
|
be32_to_cpu(adsp2_alg[i].alg.id));
|
||||||
|
@ -2114,7 +2298,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
||||||
dsp->card = codec->component.card;
|
dsp->codec = codec;
|
||||||
|
|
||||||
mutex_lock(&dsp->pwr_lock);
|
mutex_lock(&dsp->pwr_lock);
|
||||||
|
|
||||||
|
@ -2325,8 +2509,6 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
||||||
struct wm_adsp *dsp = &dsps[w->shift];
|
struct wm_adsp *dsp = &dsps[w->shift];
|
||||||
struct wm_coeff_ctl *ctl;
|
struct wm_coeff_ctl *ctl;
|
||||||
|
|
||||||
dsp->card = codec->component.card;
|
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case SND_SOC_DAPM_PRE_PMU:
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
wm_adsp2_set_dspclk(dsp, freq);
|
wm_adsp2_set_dspclk(dsp, freq);
|
||||||
|
@ -2393,14 +2575,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
|
|
||||||
mutex_lock(&dsp->pwr_lock);
|
mutex_lock(&dsp->pwr_lock);
|
||||||
|
|
||||||
if (wm_adsp_fw[dsp->fw].num_caps != 0)
|
if (wm_adsp_fw[dsp->fw].num_caps != 0) {
|
||||||
ret = wm_adsp_buffer_init(dsp);
|
ret = wm_adsp_buffer_init(dsp);
|
||||||
|
if (ret < 0) {
|
||||||
|
mutex_unlock(&dsp->pwr_lock);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&dsp->pwr_lock);
|
mutex_unlock(&dsp->pwr_lock);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_SOC_DAPM_PRE_PMD:
|
case SND_SOC_DAPM_PRE_PMD:
|
||||||
|
/* Tell the firmware to cleanup */
|
||||||
|
wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
|
||||||
|
|
||||||
/* Log firmware state, it can be useful for analysis */
|
/* Log firmware state, it can be useful for analysis */
|
||||||
wm_adsp2_show_fw_status(dsp);
|
wm_adsp2_show_fw_status(dsp);
|
||||||
|
|
||||||
|
@ -2441,6 +2631,8 @@ EXPORT_SYMBOL_GPL(wm_adsp2_event);
|
||||||
|
|
||||||
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
|
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
|
dsp->codec = codec;
|
||||||
|
|
||||||
wm_adsp2_init_debugfs(dsp, codec);
|
wm_adsp2_init_debugfs(dsp, codec);
|
||||||
|
|
||||||
return snd_soc_add_codec_controls(codec,
|
return snd_soc_add_codec_controls(codec,
|
||||||
|
|
|
@ -44,7 +44,7 @@ struct wm_adsp {
|
||||||
int type;
|
int type;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct snd_soc_card *card;
|
struct snd_soc_codec *codec;
|
||||||
|
|
||||||
int base;
|
int base;
|
||||||
int sysclk_reg;
|
int sysclk_reg;
|
||||||
|
@ -110,18 +110,17 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
||||||
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event);
|
struct snd_kcontrol *kcontrol, int event);
|
||||||
|
|
||||||
extern int wm_adsp_compr_open(struct wm_adsp *dsp,
|
int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);
|
||||||
struct snd_compr_stream *stream);
|
int wm_adsp_compr_free(struct snd_compr_stream *stream);
|
||||||
extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
|
int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
|
||||||
extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
|
|
||||||
struct snd_compr_params *params);
|
struct snd_compr_params *params);
|
||||||
extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
|
int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
|
||||||
struct snd_compr_caps *caps);
|
struct snd_compr_caps *caps);
|
||||||
extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
|
int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
|
||||||
extern int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
|
int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
|
||||||
extern int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
|
int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
|
||||||
struct snd_compr_tstamp *tstamp);
|
struct snd_compr_tstamp *tstamp);
|
||||||
extern int wm_adsp_compr_copy(struct snd_compr_stream *stream,
|
int wm_adsp_compr_copy(struct snd_compr_stream *stream,
|
||||||
char __user *buf, size_t count);
|
char __user *buf, size_t count);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
#define WMFW_CTL_FLAG_WRITEABLE 0x0002
|
#define WMFW_CTL_FLAG_WRITEABLE 0x0002
|
||||||
#define WMFW_CTL_FLAG_READABLE 0x0001
|
#define WMFW_CTL_FLAG_READABLE 0x0001
|
||||||
|
|
||||||
|
/* Non-ALSA coefficient types start at 0x1000 */
|
||||||
|
#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */
|
||||||
|
#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
|
||||||
|
|
||||||
struct wmfw_header {
|
struct wmfw_header {
|
||||||
char magic[4];
|
char magic[4];
|
||||||
__le32 len;
|
__le32 len;
|
||||||
|
|
Loading…
Add table
Reference in a new issue