Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
54643897
Commit
54643897
authored
Jun 17, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/wm8994' into asoc-next
parents
f57019aa
2da1c4bf
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
161 additions
and
43 deletions
+161
-43
include/linux/mfd/wm8994/pdata.h
include/linux/mfd/wm8994/pdata.h
+5
-0
include/linux/mfd/wm8994/registers.h
include/linux/mfd/wm8994/registers.h
+8
-0
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.c
+145
-43
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm8994.h
+3
-0
No files found.
include/linux/mfd/wm8994/pdata.h
View file @
54643897
...
...
@@ -182,6 +182,11 @@ struct wm8994_pdata {
*/
int
micdet_delay
;
/* Delay between microphone detect completing and reporting on
* insert (specified in ms)
*/
int
mic_id_delay
;
/* IRQ for microphone detection if brought out directly as a
* signal.
*/
...
...
include/linux/mfd/wm8994/registers.h
View file @
54643897
...
...
@@ -2668,6 +2668,10 @@
/*
* R772 (0x304) - AIF1ADC LRCLK
*/
#define WM8958_AIF1_LRCLK_INV 0x1000
/* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_MASK 0x1000
/* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_SHIFT 12
/* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_WIDTH 1
/* AIF1_LRCLK_INV */
#define WM8994_AIF1ADC_LRCLK_DIR 0x0800
/* AIF1ADC_LRCLK_DIR */
#define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800
/* AIF1ADC_LRCLK_DIR */
#define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11
/* AIF1ADC_LRCLK_DIR */
...
...
@@ -2679,6 +2683,10 @@
/*
* R773 (0x305) - AIF1DAC LRCLK
*/
#define WM8958_AIF1_LRCLK_INV 0x1000
/* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_MASK 0x1000
/* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_SHIFT 12
/* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_WIDTH 1
/* AIF1_LRCLK_INV */
#define WM8994_AIF1DAC_LRCLK_DIR 0x0800
/* AIF1DAC_LRCLK_DIR */
#define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800
/* AIF1DAC_LRCLK_DIR */
#define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11
/* AIF1DAC_LRCLK_DIR */
...
...
sound/soc/codecs/wm8994.c
View file @
54643897
...
...
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
...
...
@@ -1498,6 +1499,24 @@ static const char *aif1dac_text[] = {
"AIF1DACDAT"
,
"AIF3DACDAT"
,
};
static
const
char
*
loopback_text
[]
=
{
"None"
,
"ADCDAT"
,
};
static
const
struct
soc_enum
aif1_loopback_enum
=
SOC_ENUM_SINGLE
(
WM8994_AIF1_CONTROL_2
,
WM8994_AIF1_LOOPBACK_SHIFT
,
2
,
loopback_text
);
static
const
struct
snd_kcontrol_new
aif1_loopback
=
SOC_DAPM_ENUM
(
"AIF1 Loopback"
,
aif1_loopback_enum
);
static
const
struct
soc_enum
aif2_loopback_enum
=
SOC_ENUM_SINGLE
(
WM8994_AIF2_CONTROL_2
,
WM8994_AIF2_LOOPBACK_SHIFT
,
2
,
loopback_text
);
static
const
struct
snd_kcontrol_new
aif2_loopback
=
SOC_DAPM_ENUM
(
"AIF2 Loopback"
,
aif2_loopback_enum
);
static
const
struct
soc_enum
aif1dac_enum
=
SOC_ENUM_SINGLE
(
WM8994_POWER_MANAGEMENT_6
,
0
,
2
,
aif1dac_text
);
...
...
@@ -1744,6 +1763,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_ADC
(
"ADCL"
,
NULL
,
SND_SOC_NOPM
,
1
,
0
),
SND_SOC_DAPM_ADC
(
"ADCR"
,
NULL
,
SND_SOC_NOPM
,
0
,
0
),
SND_SOC_DAPM_MUX
(
"AIF1 Loopback"
,
SND_SOC_NOPM
,
0
,
0
,
&
aif1_loopback
),
SND_SOC_DAPM_MUX
(
"AIF2 Loopback"
,
SND_SOC_NOPM
,
0
,
0
,
&
aif2_loopback
),
SND_SOC_DAPM_POST
(
"Debug log"
,
post_ev
),
};
...
...
@@ -1875,9 +1897,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{
"AIF1DAC2L"
,
NULL
,
"AIF1DAC Mux"
},
{
"AIF1DAC2R"
,
NULL
,
"AIF1DAC Mux"
},
{
"AIF1DAC Mux"
,
"AIF1DACDAT"
,
"AIF1
DACDAT
"
},
{
"AIF1DAC Mux"
,
"AIF1DACDAT"
,
"AIF1
Loopback
"
},
{
"AIF1DAC Mux"
,
"AIF3DACDAT"
,
"AIF3DACDAT"
},
{
"AIF2DAC Mux"
,
"AIF2DACDAT"
,
"AIF2
DACDAT
"
},
{
"AIF2DAC Mux"
,
"AIF2DACDAT"
,
"AIF2
Loopback
"
},
{
"AIF2DAC Mux"
,
"AIF3DACDAT"
,
"AIF3DACDAT"
},
{
"AIF2ADC Mux"
,
"AIF2ADCDAT"
,
"AIF2ADCL"
},
{
"AIF2ADC Mux"
,
"AIF2ADCDAT"
,
"AIF2ADCR"
},
...
...
@@ -1928,6 +1950,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{
"AIF3ADCDAT"
,
"AIF2DACDAT"
,
"AIF2DACL"
},
{
"AIF3ADCDAT"
,
"AIF2DACDAT"
,
"AIF2DACR"
},
/* Loopback */
{
"AIF1 Loopback"
,
"ADCDAT"
,
"AIF1ADCDAT"
},
{
"AIF1 Loopback"
,
"None"
,
"AIF1DACDAT"
},
{
"AIF2 Loopback"
,
"ADCDAT"
,
"AIF2ADCDAT"
},
{
"AIF2 Loopback"
,
"None"
,
"AIF2DACDAT"
},
/* Sidetone */
{
"Left Sidetone"
,
"ADC/DMIC1"
,
"ADCL Mux"
},
{
"Left Sidetone"
,
"DMIC2"
,
"DMIC2L"
},
...
...
@@ -2010,15 +2038,16 @@ struct fll_div {
u16
outdiv
;
u16
n
;
u16
k
;
u16
lambda
;
u16
clk_ref_div
;
u16
fll_fratio
;
};
static
int
wm8994_get_fll_config
(
struct
fll_div
*
fll
,
static
int
wm8994_get_fll_config
(
struct
wm8994
*
control
,
struct
fll_div
*
fll
,
int
freq_in
,
int
freq_out
)
{
u64
Kpart
;
unsigned
int
K
,
Ndiv
,
Nmod
;
unsigned
int
K
,
Ndiv
,
Nmod
,
gcd_fll
;
pr_debug
(
"FLL input=%dHz, output=%dHz
\n
"
,
freq_in
,
freq_out
);
...
...
@@ -2067,20 +2096,32 @@ static int wm8994_get_fll_config(struct fll_div *fll,
Nmod
=
freq_out
%
freq_in
;
pr_debug
(
"Nmod=%d
\n
"
,
Nmod
);
/* Calculate fractional part - scale up so we can round. */
Kpart
=
FIXED_FLL_SIZE
*
(
long
long
)
Nmod
;
switch
(
control
->
type
)
{
case
WM8994
:
/* Calculate fractional part - scale up so we can round. */
Kpart
=
FIXED_FLL_SIZE
*
(
long
long
)
Nmod
;
do_div
(
Kpart
,
freq_in
);
K
=
Kpart
&
0xFFFFFFFF
;
do_div
(
Kpart
,
freq_in
);
if
((
K
%
10
)
>=
5
)
K
+=
5
;
K
=
Kpart
&
0xFFFFFFFF
;
/* Move down to proper range now rounding is done */
fll
->
k
=
K
/
10
;
fll
->
lambda
=
0
;
if
((
K
%
10
)
>=
5
)
K
+=
5
;
pr_debug
(
"N=%x K=%x
\n
"
,
fll
->
n
,
fll
->
k
);
break
;
/* Move down to proper range now rounding is done */
fll
->
k
=
K
/
10
;
default:
gcd_fll
=
gcd
(
freq_out
,
freq_in
)
;
pr_debug
(
"N=%x K=%x
\n
"
,
fll
->
n
,
fll
->
k
);
fll
->
k
=
(
freq_out
-
(
freq_in
*
fll
->
n
))
/
gcd_fll
;
fll
->
lambda
=
freq_in
/
gcd_fll
;
}
return
0
;
}
...
...
@@ -2144,9 +2185,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
* analysis bugs spewing warnings.
*/
if
(
freq_out
)
ret
=
wm8994_get_fll_config
(
&
fll
,
freq_in
,
freq_out
);
ret
=
wm8994_get_fll_config
(
control
,
&
fll
,
freq_in
,
freq_out
);
else
ret
=
wm8994_get_fll_config
(
&
fll
,
wm8994
->
fll
[
id
].
in
,
ret
=
wm8994_get_fll_config
(
control
,
&
fll
,
wm8994
->
fll
[
id
].
in
,
wm8994
->
fll
[
id
].
out
);
if
(
ret
<
0
)
return
ret
;
...
...
@@ -2191,6 +2232,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
WM8994_FLL1_N_MASK
,
fll
.
n
<<
WM8994_FLL1_N_SHIFT
);
if
(
fll
.
lambda
)
{
snd_soc_update_bits
(
codec
,
WM8958_FLL1_EFS_1
+
reg_offset
,
WM8958_FLL1_LAMBDA_MASK
,
fll
.
lambda
);
snd_soc_update_bits
(
codec
,
WM8958_FLL1_EFS_2
+
reg_offset
,
WM8958_FLL1_EFS_ENA
,
WM8958_FLL1_EFS_ENA
);
}
else
{
snd_soc_update_bits
(
codec
,
WM8958_FLL1_EFS_2
+
reg_offset
,
WM8958_FLL1_EFS_ENA
,
0
);
}
snd_soc_update_bits
(
codec
,
WM8994_FLL1_CONTROL_5
+
reg_offset
,
WM8994_FLL1_FRC_NCO
|
WM8958_FLL1_BYP
|
WM8994_FLL1_REFCLK_DIV_MASK
|
...
...
@@ -2555,17 +2607,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
ms_reg
;
int
aif1_reg
;
int
dac_reg
;
int
adc_reg
;
int
ms
=
0
;
int
aif1
=
0
;
int
lrclk
=
0
;
switch
(
dai
->
id
)
{
case
1
:
ms_reg
=
WM8994_AIF1_MASTER_SLAVE
;
aif1_reg
=
WM8994_AIF1_CONTROL_1
;
dac_reg
=
WM8994_AIF1DAC_LRCLK
;
adc_reg
=
WM8994_AIF1ADC_LRCLK
;
break
;
case
2
:
ms_reg
=
WM8994_AIF2_MASTER_SLAVE
;
aif1_reg
=
WM8994_AIF2_CONTROL_1
;
dac_reg
=
WM8994_AIF1DAC_LRCLK
;
adc_reg
=
WM8994_AIF1ADC_LRCLK
;
break
;
default:
return
-
EINVAL
;
...
...
@@ -2584,6 +2643,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch
(
fmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
{
case
SND_SOC_DAIFMT_DSP_B
:
aif1
|=
WM8994_AIF1_LRCLK_INV
;
lrclk
|=
WM8958_AIF1_LRCLK_INV
;
case
SND_SOC_DAIFMT_DSP_A
:
aif1
|=
0x18
;
break
;
...
...
@@ -2622,12 +2682,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break
;
case
SND_SOC_DAIFMT_IB_IF
:
aif1
|=
WM8994_AIF1_BCLK_INV
|
WM8994_AIF1_LRCLK_INV
;
lrclk
|=
WM8958_AIF1_LRCLK_INV
;
break
;
case
SND_SOC_DAIFMT_IB_NF
:
aif1
|=
WM8994_AIF1_BCLK_INV
;
break
;
case
SND_SOC_DAIFMT_NB_IF
:
aif1
|=
WM8994_AIF1_LRCLK_INV
;
lrclk
|=
WM8958_AIF1_LRCLK_INV
;
break
;
default:
return
-
EINVAL
;
...
...
@@ -2658,6 +2720,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
aif1
);
snd_soc_update_bits
(
codec
,
ms_reg
,
WM8994_AIF1_MSTR
,
ms
);
snd_soc_update_bits
(
codec
,
dac_reg
,
WM8958_AIF1_LRCLK_INV
,
lrclk
);
snd_soc_update_bits
(
codec
,
adc_reg
,
WM8958_AIF1_LRCLK_INV
,
lrclk
);
return
0
;
}
...
...
@@ -3096,24 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
static
int
wm8994_codec_resume
(
struct
snd_soc_codec
*
codec
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
i
,
ret
;
unsigned
int
val
,
mask
;
if
(
control
->
revision
<
4
)
{
/* force a HW read */
ret
=
regmap_read
(
control
->
regmap
,
WM8994_POWER_MANAGEMENT_5
,
&
val
);
/* modify the cache only */
codec
->
cache_only
=
1
;
mask
=
WM8994_DAC1R_ENA
|
WM8994_DAC1L_ENA
|
WM8994_DAC2R_ENA
|
WM8994_DAC2L_ENA
;
val
&=
mask
;
snd_soc_update_bits
(
codec
,
WM8994_POWER_MANAGEMENT_5
,
mask
,
val
);
codec
->
cache_only
=
0
;
}
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
wm8994
->
fll
);
i
++
)
{
if
(
!
wm8994
->
fll_suspend
[
i
].
out
)
...
...
@@ -3495,6 +3544,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
wm8994
->
btn_mask
);
}
static
void
wm8958_open_circuit_work
(
struct
work_struct
*
work
)
{
struct
wm8994_priv
*
wm8994
=
container_of
(
work
,
struct
wm8994_priv
,
open_circuit_work
.
work
);
struct
device
*
dev
=
wm8994
->
wm8994
->
dev
;
wm1811_micd_stop
(
wm8994
->
hubs
.
codec
);
mutex_lock
(
&
wm8994
->
accdet_lock
);
dev_dbg
(
dev
,
"Reporting open circuit
\n
"
);
wm8994
->
jack_mic
=
false
;
wm8994
->
mic_detecting
=
true
;
wm8958_micd_set_rate
(
wm8994
->
hubs
.
codec
);
snd_soc_jack_report
(
wm8994
->
micdet
[
0
].
jack
,
0
,
wm8994
->
btn_mask
|
SND_JACK_HEADSET
);
mutex_unlock
(
&
wm8994
->
accdet_lock
);
}
static
void
wm8958_mic_id
(
void
*
data
,
u16
status
)
{
struct
snd_soc_codec
*
codec
=
data
;
...
...
@@ -3504,16 +3578,9 @@ static void wm8958_mic_id(void *data, u16 status)
if
(
!
(
status
&
WM8958_MICD_STS
))
{
/* If nothing present then clear our statuses */
dev_dbg
(
codec
->
dev
,
"Detected open circuit
\n
"
);
wm8994
->
jack_mic
=
false
;
wm8994
->
mic_detecting
=
true
;
wm1811_micd_stop
(
codec
);
wm8958_micd_set_rate
(
codec
);
snd_soc_jack_report
(
wm8994
->
micdet
[
0
].
jack
,
0
,
wm8994
->
btn_mask
|
SND_JACK_HEADSET
);
schedule_delayed_work
(
&
wm8994
->
open_circuit_work
,
msecs_to_jiffies
(
2500
));
return
;
}
...
...
@@ -3598,6 +3665,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
pm_runtime_get_sync
(
codec
->
dev
);
cancel_delayed_work_sync
(
&
wm8994
->
mic_complete_work
);
mutex_lock
(
&
wm8994
->
accdet_lock
);
reg
=
snd_soc_read
(
codec
,
WM1811_JACKDET_CTRL
);
...
...
@@ -3780,11 +3849,33 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
EXPORT_SYMBOL_GPL
(
wm8958_mic_detect
);
static
void
wm8958_mic_work
(
struct
work_struct
*
work
)
{
struct
wm8994_priv
*
wm8994
=
container_of
(
work
,
struct
wm8994_priv
,
mic_complete_work
.
work
);
struct
snd_soc_codec
*
codec
=
wm8994
->
hubs
.
codec
;
dev_crit
(
codec
->
dev
,
"MIC WORK %x
\n
"
,
wm8994
->
mic_status
);
pm_runtime_get_sync
(
codec
->
dev
);
mutex_lock
(
&
wm8994
->
accdet_lock
);
wm8994
->
mic_id_cb
(
wm8994
->
mic_id_cb_data
,
wm8994
->
mic_status
);
mutex_unlock
(
&
wm8994
->
accdet_lock
);
pm_runtime_put
(
codec
->
dev
);
dev_crit
(
codec
->
dev
,
"MIC WORK %x DONE
\n
"
,
wm8994
->
mic_status
);
}
static
irqreturn_t
wm8958_mic_irq
(
int
irq
,
void
*
data
)
{
struct
wm8994_priv
*
wm8994
=
data
;
struct
snd_soc_codec
*
codec
=
wm8994
->
hubs
.
codec
;
int
reg
,
count
,
ret
;
int
reg
,
count
,
ret
,
id_delay
;
/*
* Jack detection may have detected a removal simulataneously
...
...
@@ -3794,6 +3885,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
if
(
!
(
snd_soc_read
(
codec
,
WM8958_MIC_DETECT_1
)
&
WM8958_MICD_ENA
))
return
IRQ_HANDLED
;
cancel_delayed_work_sync
(
&
wm8994
->
mic_complete_work
);
cancel_delayed_work_sync
(
&
wm8994
->
open_circuit_work
);
pm_runtime_get_sync
(
codec
->
dev
);
/* We may occasionally read a detection without an impedence
...
...
@@ -3846,8 +3940,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
goto
out
;
}
wm8994
->
mic_status
=
reg
;
id_delay
=
wm8994
->
wm8994
->
pdata
.
mic_id_delay
;
if
(
wm8994
->
mic_detecting
)
wm8994
->
mic_id_cb
(
wm8994
->
mic_id_cb_data
,
reg
);
schedule_delayed_work
(
&
wm8994
->
mic_complete_work
,
msecs_to_jiffies
(
id_delay
));
else
wm8958_button_det
(
codec
,
reg
);
...
...
@@ -3899,6 +3997,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
mutex_init
(
&
wm8994
->
accdet_lock
);
INIT_DELAYED_WORK
(
&
wm8994
->
jackdet_bootstrap
,
wm1811_jackdet_bootstrap
);
INIT_DELAYED_WORK
(
&
wm8994
->
open_circuit_work
,
wm8958_open_circuit_work
);
switch
(
control
->
type
)
{
case
WM8994
:
...
...
@@ -3911,6 +4011,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break
;
}
INIT_DELAYED_WORK
(
&
wm8994
->
mic_complete_work
,
wm8958_mic_work
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
wm8994
->
fll_locked
);
i
++
)
init_completion
(
&
wm8994
->
fll_locked
[
i
]);
...
...
sound/soc/codecs/wm8994.h
View file @
54643897
...
...
@@ -134,6 +134,9 @@ struct wm8994_priv {
struct
mutex
accdet_lock
;
struct
wm8994_micdet
micdet
[
2
];
struct
delayed_work
mic_work
;
struct
delayed_work
open_circuit_work
;
struct
delayed_work
mic_complete_work
;
u16
mic_status
;
bool
mic_detecting
;
bool
jack_mic
;
int
btn_mask
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment