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
nexedi
linux
Commits
37cfa195
Commit
37cfa195
authored
Aug 15, 2009
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'wm8974-upstream' into for-2.6.32
parents
29e02cb3
25cbf465
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
952 additions
and
0 deletions
+952
-0
sound/soc/codecs/Kconfig
sound/soc/codecs/Kconfig
+4
-0
sound/soc/codecs/Makefile
sound/soc/codecs/Makefile
+2
-0
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8974.c
+847
-0
sound/soc/codecs/wm8974.h
sound/soc/codecs/wm8974.h
+99
-0
No files found.
sound/soc/codecs/Kconfig
View file @
37cfa195
...
...
@@ -46,6 +46,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8960 if I2C
select SND_SOC_WM8961 if I2C
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8974 if I2C
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
select SND_SOC_WM8993 if I2C
...
...
@@ -188,6 +189,9 @@ config SND_SOC_WM8961
config SND_SOC_WM8971
tristate
config SND_SOC_WM8974
tristate
config SND_SOC_WM8988
tristate
...
...
sound/soc/codecs/Makefile
View file @
37cfa195
...
...
@@ -34,6 +34,7 @@ snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8960-objs
:=
wm8960.o
snd-soc-wm8961-objs
:=
wm8961.o
snd-soc-wm8971-objs
:=
wm8971.o
snd-soc-wm8974-objs
:=
wm8974.o
snd-soc-wm8988-objs
:=
wm8988.o
snd-soc-wm8990-objs
:=
wm8990.o
snd-soc-wm8993-objs
:=
wm8993.o
...
...
@@ -79,6 +80,7 @@ obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8900)
+=
snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903)
+=
snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971)
+=
snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974)
+=
snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8940)
+=
snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8960)
+=
snd-soc-wm8960.o
obj-$(CONFIG_SND_SOC_WM8961)
+=
snd-soc-wm8961.o
...
...
sound/soc/codecs/wm8974.c
0 → 100644
View file @
37cfa195
/*
* wm8974.c -- WM8974 ALSA Soc Audio driver
*
* Copyright 2006-2009 Wolfson Microelectronics PLC.
*
* Author: Liam Girdwood <linux@wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "wm8974.h"
static
const
u16
wm8974_reg
[
WM8974_CACHEREGNUM
]
=
{
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0050
,
0x0000
,
0x0140
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x00ff
,
0x0000
,
0x0000
,
0x0100
,
0x00ff
,
0x0000
,
0x0000
,
0x012c
,
0x002c
,
0x002c
,
0x002c
,
0x002c
,
0x0000
,
0x0032
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0038
,
0x000b
,
0x0032
,
0x0000
,
0x0008
,
0x000c
,
0x0093
,
0x00e9
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0003
,
0x0010
,
0x0000
,
0x0000
,
0x0000
,
0x0002
,
0x0000
,
0x0000
,
0x0000
,
0x0000
,
0x0039
,
0x0000
,
0x0000
,
};
#define WM8974_POWER1_BIASEN 0x08
#define WM8974_POWER1_BUFIOEN 0x10
struct
wm8974_priv
{
struct
snd_soc_codec
codec
;
u16
reg_cache
[
WM8974_CACHEREGNUM
];
};
static
struct
snd_soc_codec
*
wm8974_codec
;
/*
* read wm8974 register cache
*/
static
inline
unsigned
int
wm8974_read_reg_cache
(
struct
snd_soc_codec
*
codec
,
unsigned
int
reg
)
{
u16
*
cache
=
codec
->
reg_cache
;
if
(
reg
==
WM8974_RESET
)
return
0
;
if
(
reg
>=
WM8974_CACHEREGNUM
)
return
-
1
;
return
cache
[
reg
];
}
/*
* write wm8974 register cache
*/
static
inline
void
wm8974_write_reg_cache
(
struct
snd_soc_codec
*
codec
,
u16
reg
,
unsigned
int
value
)
{
u16
*
cache
=
codec
->
reg_cache
;
if
(
reg
>=
WM8974_CACHEREGNUM
)
return
;
cache
[
reg
]
=
value
;
}
/*
* write to the WM8974 register space
*/
static
int
wm8974_write
(
struct
snd_soc_codec
*
codec
,
unsigned
int
reg
,
unsigned
int
value
)
{
u8
data
[
2
];
/* data is
* D15..D9 WM8974 register offset
* D8...D0 register data
*/
data
[
0
]
=
(
reg
<<
1
)
|
((
value
>>
8
)
&
0x0001
);
data
[
1
]
=
value
&
0x00ff
;
wm8974_write_reg_cache
(
codec
,
reg
,
value
);
if
(
codec
->
hw_write
(
codec
->
control_data
,
data
,
2
)
==
2
)
return
0
;
else
return
-
EIO
;
}
#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
static
const
char
*
wm8974_companding
[]
=
{
"Off"
,
"NC"
,
"u-law"
,
"A-law"
};
static
const
char
*
wm8974_deemp
[]
=
{
"None"
,
"32kHz"
,
"44.1kHz"
,
"48kHz"
};
static
const
char
*
wm8974_eqmode
[]
=
{
"Capture"
,
"Playback"
};
static
const
char
*
wm8974_bw
[]
=
{
"Narrow"
,
"Wide"
};
static
const
char
*
wm8974_eq1
[]
=
{
"80Hz"
,
"105Hz"
,
"135Hz"
,
"175Hz"
};
static
const
char
*
wm8974_eq2
[]
=
{
"230Hz"
,
"300Hz"
,
"385Hz"
,
"500Hz"
};
static
const
char
*
wm8974_eq3
[]
=
{
"650Hz"
,
"850Hz"
,
"1.1kHz"
,
"1.4kHz"
};
static
const
char
*
wm8974_eq4
[]
=
{
"1.8kHz"
,
"2.4kHz"
,
"3.2kHz"
,
"4.1kHz"
};
static
const
char
*
wm8974_eq5
[]
=
{
"5.3kHz"
,
"6.9kHz"
,
"9kHz"
,
"11.7kHz"
};
static
const
char
*
wm8974_alc
[]
=
{
"ALC"
,
"Limiter"
};
static
const
struct
soc_enum
wm8974_enum
[]
=
{
SOC_ENUM_SINGLE
(
WM8974_COMP
,
1
,
4
,
wm8974_companding
),
/* adc */
SOC_ENUM_SINGLE
(
WM8974_COMP
,
3
,
4
,
wm8974_companding
),
/* dac */
SOC_ENUM_SINGLE
(
WM8974_DAC
,
4
,
4
,
wm8974_deemp
),
SOC_ENUM_SINGLE
(
WM8974_EQ1
,
8
,
2
,
wm8974_eqmode
),
SOC_ENUM_SINGLE
(
WM8974_EQ1
,
5
,
4
,
wm8974_eq1
),
SOC_ENUM_SINGLE
(
WM8974_EQ2
,
8
,
2
,
wm8974_bw
),
SOC_ENUM_SINGLE
(
WM8974_EQ2
,
5
,
4
,
wm8974_eq2
),
SOC_ENUM_SINGLE
(
WM8974_EQ3
,
8
,
2
,
wm8974_bw
),
SOC_ENUM_SINGLE
(
WM8974_EQ3
,
5
,
4
,
wm8974_eq3
),
SOC_ENUM_SINGLE
(
WM8974_EQ4
,
8
,
2
,
wm8974_bw
),
SOC_ENUM_SINGLE
(
WM8974_EQ4
,
5
,
4
,
wm8974_eq4
),
SOC_ENUM_SINGLE
(
WM8974_EQ5
,
8
,
2
,
wm8974_bw
),
SOC_ENUM_SINGLE
(
WM8974_EQ5
,
5
,
4
,
wm8974_eq5
),
SOC_ENUM_SINGLE
(
WM8974_ALC3
,
8
,
2
,
wm8974_alc
),
};
static
const
char
*
wm8974_auxmode_text
[]
=
{
"Buffer"
,
"Mixer"
};
static
const
struct
soc_enum
wm8974_auxmode
=
SOC_ENUM_SINGLE
(
WM8974_INPUT
,
3
,
2
,
wm8974_auxmode_text
);
static
const
DECLARE_TLV_DB_SCALE
(
digital_tlv
,
-
12750
,
50
,
1
);
static
const
DECLARE_TLV_DB_SCALE
(
eq_tlv
,
-
1200
,
100
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
inpga_tlv
,
-
1200
,
75
,
0
);
static
const
DECLARE_TLV_DB_SCALE
(
spk_tlv
,
-
5700
,
100
,
0
);
static
const
struct
snd_kcontrol_new
wm8974_snd_controls
[]
=
{
SOC_SINGLE
(
"Digital Loopback Switch"
,
WM8974_COMP
,
0
,
1
,
0
),
SOC_ENUM
(
"DAC Companding"
,
wm8974_enum
[
1
]),
SOC_ENUM
(
"ADC Companding"
,
wm8974_enum
[
0
]),
SOC_ENUM
(
"Playback De-emphasis"
,
wm8974_enum
[
2
]),
SOC_SINGLE
(
"DAC Inversion Switch"
,
WM8974_DAC
,
0
,
1
,
0
),
SOC_SINGLE_TLV
(
"PCM Volume"
,
WM8974_DACVOL
,
0
,
255
,
0
,
digital_tlv
),
SOC_SINGLE
(
"High Pass Filter Switch"
,
WM8974_ADC
,
8
,
1
,
0
),
SOC_SINGLE
(
"High Pass Cut Off"
,
WM8974_ADC
,
4
,
7
,
0
),
SOC_SINGLE
(
"ADC Inversion Switch"
,
WM8974_ADC
,
0
,
1
,
0
),
SOC_SINGLE_TLV
(
"Capture Volume"
,
WM8974_ADCVOL
,
0
,
255
,
0
,
digital_tlv
),
SOC_ENUM
(
"Equaliser Function"
,
wm8974_enum
[
3
]),
SOC_ENUM
(
"EQ1 Cut Off"
,
wm8974_enum
[
4
]),
SOC_SINGLE_TLV
(
"EQ1 Volume"
,
WM8974_EQ1
,
0
,
24
,
1
,
eq_tlv
),
SOC_ENUM
(
"Equaliser EQ2 Bandwith"
,
wm8974_enum
[
5
]),
SOC_ENUM
(
"EQ2 Cut Off"
,
wm8974_enum
[
6
]),
SOC_SINGLE_TLV
(
"EQ2 Volume"
,
WM8974_EQ2
,
0
,
24
,
1
,
eq_tlv
),
SOC_ENUM
(
"Equaliser EQ3 Bandwith"
,
wm8974_enum
[
7
]),
SOC_ENUM
(
"EQ3 Cut Off"
,
wm8974_enum
[
8
]),
SOC_SINGLE_TLV
(
"EQ3 Volume"
,
WM8974_EQ3
,
0
,
24
,
1
,
eq_tlv
),
SOC_ENUM
(
"Equaliser EQ4 Bandwith"
,
wm8974_enum
[
9
]),
SOC_ENUM
(
"EQ4 Cut Off"
,
wm8974_enum
[
10
]),
SOC_SINGLE_TLV
(
"EQ4 Volume"
,
WM8974_EQ4
,
0
,
24
,
1
,
eq_tlv
),
SOC_ENUM
(
"Equaliser EQ5 Bandwith"
,
wm8974_enum
[
11
]),
SOC_ENUM
(
"EQ5 Cut Off"
,
wm8974_enum
[
12
]),
SOC_SINGLE_TLV
(
"EQ5 Volume"
,
WM8974_EQ5
,
0
,
24
,
1
,
eq_tlv
),
SOC_SINGLE
(
"DAC Playback Limiter Switch"
,
WM8974_DACLIM1
,
8
,
1
,
0
),
SOC_SINGLE
(
"DAC Playback Limiter Decay"
,
WM8974_DACLIM1
,
4
,
15
,
0
),
SOC_SINGLE
(
"DAC Playback Limiter Attack"
,
WM8974_DACLIM1
,
0
,
15
,
0
),
SOC_SINGLE
(
"DAC Playback Limiter Threshold"
,
WM8974_DACLIM2
,
4
,
7
,
0
),
SOC_SINGLE
(
"DAC Playback Limiter Boost"
,
WM8974_DACLIM2
,
0
,
15
,
0
),
SOC_SINGLE
(
"ALC Enable Switch"
,
WM8974_ALC1
,
8
,
1
,
0
),
SOC_SINGLE
(
"ALC Capture Max Gain"
,
WM8974_ALC1
,
3
,
7
,
0
),
SOC_SINGLE
(
"ALC Capture Min Gain"
,
WM8974_ALC1
,
0
,
7
,
0
),
SOC_SINGLE
(
"ALC Capture ZC Switch"
,
WM8974_ALC2
,
8
,
1
,
0
),
SOC_SINGLE
(
"ALC Capture Hold"
,
WM8974_ALC2
,
4
,
7
,
0
),
SOC_SINGLE
(
"ALC Capture Target"
,
WM8974_ALC2
,
0
,
15
,
0
),
SOC_ENUM
(
"ALC Capture Mode"
,
wm8974_enum
[
13
]),
SOC_SINGLE
(
"ALC Capture Decay"
,
WM8974_ALC3
,
4
,
15
,
0
),
SOC_SINGLE
(
"ALC Capture Attack"
,
WM8974_ALC3
,
0
,
15
,
0
),
SOC_SINGLE
(
"ALC Capture Noise Gate Switch"
,
WM8974_NGATE
,
3
,
1
,
0
),
SOC_SINGLE
(
"ALC Capture Noise Gate Threshold"
,
WM8974_NGATE
,
0
,
7
,
0
),
SOC_SINGLE
(
"Capture PGA ZC Switch"
,
WM8974_INPPGA
,
7
,
1
,
0
),
SOC_SINGLE_TLV
(
"Capture PGA Volume"
,
WM8974_INPPGA
,
0
,
63
,
0
,
inpga_tlv
),
SOC_SINGLE
(
"Speaker Playback ZC Switch"
,
WM8974_SPKVOL
,
7
,
1
,
0
),
SOC_SINGLE
(
"Speaker Playback Switch"
,
WM8974_SPKVOL
,
6
,
1
,
1
),
SOC_SINGLE_TLV
(
"Speaker Playback Volume"
,
WM8974_SPKVOL
,
0
,
63
,
0
,
spk_tlv
),
SOC_ENUM
(
"Aux Mode"
,
wm8974_auxmode
),
SOC_SINGLE
(
"Capture Boost(+20dB)"
,
WM8974_ADCBOOST
,
8
,
1
,
0
),
SOC_SINGLE
(
"Mono Playback Switch"
,
WM8974_MONOMIX
,
6
,
1
,
1
),
};
/* Speaker Output Mixer */
static
const
struct
snd_kcontrol_new
wm8974_speaker_mixer_controls
[]
=
{
SOC_DAPM_SINGLE
(
"Line Bypass Switch"
,
WM8974_SPKMIX
,
1
,
1
,
0
),
SOC_DAPM_SINGLE
(
"Aux Playback Switch"
,
WM8974_SPKMIX
,
5
,
1
,
0
),
SOC_DAPM_SINGLE
(
"PCM Playback Switch"
,
WM8974_SPKMIX
,
0
,
1
,
1
),
};
/* Mono Output Mixer */
static
const
struct
snd_kcontrol_new
wm8974_mono_mixer_controls
[]
=
{
SOC_DAPM_SINGLE
(
"Line Bypass Switch"
,
WM8974_MONOMIX
,
1
,
1
,
0
),
SOC_DAPM_SINGLE
(
"Aux Playback Switch"
,
WM8974_MONOMIX
,
2
,
1
,
0
),
SOC_DAPM_SINGLE
(
"PCM Playback Switch"
,
WM8974_MONOMIX
,
0
,
1
,
0
),
};
/* Boost mixer */
static
const
struct
snd_kcontrol_new
wm8974_boost_mixer
[]
=
{
SOC_DAPM_SINGLE
(
"Aux Switch"
,
WM8974_INPPGA
,
6
,
1
,
0
),
};
/* Input PGA */
static
const
struct
snd_kcontrol_new
wm8974_inpga
[]
=
{
SOC_DAPM_SINGLE
(
"Aux Switch"
,
WM8974_INPUT
,
2
,
1
,
0
),
SOC_DAPM_SINGLE
(
"MicN Switch"
,
WM8974_INPUT
,
1
,
1
,
0
),
SOC_DAPM_SINGLE
(
"MicP Switch"
,
WM8974_INPUT
,
0
,
1
,
0
),
};
/* AUX Input boost vol */
static
const
struct
snd_kcontrol_new
wm8974_aux_boost_controls
=
SOC_DAPM_SINGLE
(
"Aux Volume"
,
WM8974_ADCBOOST
,
0
,
7
,
0
);
/* Mic Input boost vol */
static
const
struct
snd_kcontrol_new
wm8974_mic_boost_controls
=
SOC_DAPM_SINGLE
(
"Mic Volume"
,
WM8974_ADCBOOST
,
4
,
7
,
0
);
static
const
struct
snd_soc_dapm_widget
wm8974_dapm_widgets
[]
=
{
SND_SOC_DAPM_MIXER
(
"Speaker Mixer"
,
WM8974_POWER3
,
2
,
0
,
&
wm8974_speaker_mixer_controls
[
0
],
ARRAY_SIZE
(
wm8974_speaker_mixer_controls
)),
SND_SOC_DAPM_MIXER
(
"Mono Mixer"
,
WM8974_POWER3
,
3
,
0
,
&
wm8974_mono_mixer_controls
[
0
],
ARRAY_SIZE
(
wm8974_mono_mixer_controls
)),
SND_SOC_DAPM_DAC
(
"DAC"
,
"HiFi Playback"
,
WM8974_POWER3
,
0
,
0
),
SND_SOC_DAPM_ADC
(
"ADC"
,
"HiFi Capture"
,
WM8974_POWER2
,
0
,
0
),
SND_SOC_DAPM_PGA
(
"Aux Input"
,
WM8974_POWER1
,
6
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"SpkN Out"
,
WM8974_POWER3
,
5
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"SpkP Out"
,
WM8974_POWER3
,
6
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"Mono Out"
,
WM8974_POWER3
,
7
,
0
,
NULL
,
0
),
SND_SOC_DAPM_MIXER
(
"Input PGA"
,
WM8974_POWER2
,
2
,
0
,
wm8974_inpga
,
ARRAY_SIZE
(
wm8974_inpga
)),
SND_SOC_DAPM_MIXER
(
"Boost Mixer"
,
WM8974_POWER2
,
4
,
0
,
wm8974_boost_mixer
,
ARRAY_SIZE
(
wm8974_boost_mixer
)),
SND_SOC_DAPM_MICBIAS
(
"Mic Bias"
,
WM8974_POWER1
,
4
,
0
),
SND_SOC_DAPM_INPUT
(
"MICN"
),
SND_SOC_DAPM_INPUT
(
"MICP"
),
SND_SOC_DAPM_INPUT
(
"AUX"
),
SND_SOC_DAPM_OUTPUT
(
"MONOOUT"
),
SND_SOC_DAPM_OUTPUT
(
"SPKOUTP"
),
SND_SOC_DAPM_OUTPUT
(
"SPKOUTN"
),
};
static
const
struct
snd_soc_dapm_route
audio_map
[]
=
{
/* Mono output mixer */
{
"Mono Mixer"
,
"PCM Playback Switch"
,
"DAC"
},
{
"Mono Mixer"
,
"Aux Playback Switch"
,
"Aux Input"
},
{
"Mono Mixer"
,
"Line Bypass Switch"
,
"Boost Mixer"
},
/* Speaker output mixer */
{
"Speaker Mixer"
,
"PCM Playback Switch"
,
"DAC"
},
{
"Speaker Mixer"
,
"Aux Playback Switch"
,
"Aux Input"
},
{
"Speaker Mixer"
,
"Line Bypass Switch"
,
"Boost Mixer"
},
/* Outputs */
{
"Mono Out"
,
NULL
,
"Mono Mixer"
},
{
"MONOOUT"
,
NULL
,
"Mono Out"
},
{
"SpkN Out"
,
NULL
,
"Speaker Mixer"
},
{
"SpkP Out"
,
NULL
,
"Speaker Mixer"
},
{
"SPKOUTN"
,
NULL
,
"SpkN Out"
},
{
"SPKOUTP"
,
NULL
,
"SpkP Out"
},
/* Boost Mixer */
{
"ADC"
,
NULL
,
"Boost Mixer"
},
{
"Boost Mixer"
,
"Aux Switch"
,
"Aux Input"
},
{
"Boost Mixer"
,
NULL
,
"Input PGA"
},
{
"Boost Mixer"
,
NULL
,
"MICP"
},
/* Input PGA */
{
"Input PGA"
,
"Aux Switch"
,
"Aux Input"
},
{
"Input PGA"
,
"MicN Switch"
,
"MICN"
},
{
"Input PGA"
,
"MicP Switch"
,
"MICP"
},
/* Inputs */
{
"Aux Input"
,
NULL
,
"AUX"
},
};
static
int
wm8974_add_widgets
(
struct
snd_soc_codec
*
codec
)
{
snd_soc_dapm_new_controls
(
codec
,
wm8974_dapm_widgets
,
ARRAY_SIZE
(
wm8974_dapm_widgets
));
snd_soc_dapm_add_routes
(
codec
,
audio_map
,
ARRAY_SIZE
(
audio_map
));
snd_soc_dapm_new_widgets
(
codec
);
return
0
;
}
struct
pll_
{
unsigned
int
pre_div
:
4
;
/* prescale - 1 */
unsigned
int
n
:
4
;
unsigned
int
k
;
};
static
struct
pll_
pll_div
;
/* The size in bits of the pll divide multiplied by 10
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 24) * 10)
static
void
pll_factors
(
unsigned
int
target
,
unsigned
int
source
)
{
unsigned
long
long
Kpart
;
unsigned
int
K
,
Ndiv
,
Nmod
;
Ndiv
=
target
/
source
;
if
(
Ndiv
<
6
)
{
source
>>=
1
;
pll_div
.
pre_div
=
1
;
Ndiv
=
target
/
source
;
}
else
pll_div
.
pre_div
=
0
;
if
((
Ndiv
<
6
)
||
(
Ndiv
>
12
))
printk
(
KERN_WARNING
"WM8974 N value %u outwith recommended range!
\n
"
,
Ndiv
);
pll_div
.
n
=
Ndiv
;
Nmod
=
target
%
source
;
Kpart
=
FIXED_PLL_SIZE
*
(
long
long
)
Nmod
;
do_div
(
Kpart
,
source
);
K
=
Kpart
&
0xFFFFFFFF
;
/* Check if we need to round */
if
((
K
%
10
)
>=
5
)
K
+=
5
;
/* Move down to proper range now rounding is done */
K
/=
10
;
pll_div
.
k
=
K
;
}
static
int
wm8974_set_dai_pll
(
struct
snd_soc_dai
*
codec_dai
,
int
pll_id
,
unsigned
int
freq_in
,
unsigned
int
freq_out
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
u16
reg
;
if
(
freq_in
==
0
||
freq_out
==
0
)
{
/* Clock CODEC directly from MCLK */
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_CLOCK
);
wm8974_write
(
codec
,
WM8974_CLOCK
,
reg
&
0x0ff
);
/* Turn off PLL */
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_POWER1
);
wm8974_write
(
codec
,
WM8974_POWER1
,
reg
&
0x1df
);
return
0
;
}
pll_factors
(
freq_out
*
4
,
freq_in
);
wm8974_write
(
codec
,
WM8974_PLLN
,
(
pll_div
.
pre_div
<<
4
)
|
pll_div
.
n
);
wm8974_write
(
codec
,
WM8974_PLLK1
,
pll_div
.
k
>>
18
);
wm8974_write
(
codec
,
WM8974_PLLK2
,
(
pll_div
.
k
>>
9
)
&
0x1ff
);
wm8974_write
(
codec
,
WM8974_PLLK3
,
pll_div
.
k
&
0x1ff
);
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_POWER1
);
wm8974_write
(
codec
,
WM8974_POWER1
,
reg
|
0x020
);
/* Run CODEC from PLL instead of MCLK */
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_CLOCK
);
wm8974_write
(
codec
,
WM8974_CLOCK
,
reg
|
0x100
);
return
0
;
}
/*
* Configure WM8974 clock dividers.
*/
static
int
wm8974_set_dai_clkdiv
(
struct
snd_soc_dai
*
codec_dai
,
int
div_id
,
int
div
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
u16
reg
;
switch
(
div_id
)
{
case
WM8974_OPCLKDIV
:
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_GPIO
)
&
0x1cf
;
wm8974_write
(
codec
,
WM8974_GPIO
,
reg
|
div
);
break
;
case
WM8974_MCLKDIV
:
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_CLOCK
)
&
0x11f
;
wm8974_write
(
codec
,
WM8974_CLOCK
,
reg
|
div
);
break
;
case
WM8974_ADCCLK
:
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_ADC
)
&
0x1f7
;
wm8974_write
(
codec
,
WM8974_ADC
,
reg
|
div
);
break
;
case
WM8974_DACCLK
:
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_DAC
)
&
0x1f7
;
wm8974_write
(
codec
,
WM8974_DAC
,
reg
|
div
);
break
;
case
WM8974_BCLKDIV
:
reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_CLOCK
)
&
0x1e3
;
wm8974_write
(
codec
,
WM8974_CLOCK
,
reg
|
div
);
break
;
default:
return
-
EINVAL
;
}
return
0
;
}
static
int
wm8974_set_dai_fmt
(
struct
snd_soc_dai
*
codec_dai
,
unsigned
int
fmt
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
u16
iface
=
0
;
u16
clk
=
wm8974_read_reg_cache
(
codec
,
WM8974_CLOCK
)
&
0x1fe
;
/* set master/slave audio interface */
switch
(
fmt
&
SND_SOC_DAIFMT_MASTER_MASK
)
{
case
SND_SOC_DAIFMT_CBM_CFM
:
clk
|=
0x0001
;
break
;
case
SND_SOC_DAIFMT_CBS_CFS
:
break
;
default:
return
-
EINVAL
;
}
/* interface format */
switch
(
fmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
{
case
SND_SOC_DAIFMT_I2S
:
iface
|=
0x0010
;
break
;
case
SND_SOC_DAIFMT_RIGHT_J
:
break
;
case
SND_SOC_DAIFMT_LEFT_J
:
iface
|=
0x0008
;
break
;
case
SND_SOC_DAIFMT_DSP_A
:
iface
|=
0x00018
;
break
;
default:
return
-
EINVAL
;
}
/* clock inversion */
switch
(
fmt
&
SND_SOC_DAIFMT_INV_MASK
)
{
case
SND_SOC_DAIFMT_NB_NF
:
break
;
case
SND_SOC_DAIFMT_IB_IF
:
iface
|=
0x0180
;
break
;
case
SND_SOC_DAIFMT_IB_NF
:
iface
|=
0x0100
;
break
;
case
SND_SOC_DAIFMT_NB_IF
:
iface
|=
0x0080
;
break
;
default:
return
-
EINVAL
;
}
wm8974_write
(
codec
,
WM8974_IFACE
,
iface
);
wm8974_write
(
codec
,
WM8974_CLOCK
,
clk
);
return
0
;
}
static
int
wm8974_pcm_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
u16
iface
=
wm8974_read_reg_cache
(
codec
,
WM8974_IFACE
)
&
0x19f
;
u16
adn
=
wm8974_read_reg_cache
(
codec
,
WM8974_ADD
)
&
0x1f1
;
/* bit size */
switch
(
params_format
(
params
))
{
case
SNDRV_PCM_FORMAT_S16_LE
:
break
;
case
SNDRV_PCM_FORMAT_S20_3LE
:
iface
|=
0x0020
;
break
;
case
SNDRV_PCM_FORMAT_S24_LE
:
iface
|=
0x0040
;
break
;
case
SNDRV_PCM_FORMAT_S32_LE
:
iface
|=
0x0060
;
break
;
}
/* filter coefficient */
switch
(
params_rate
(
params
))
{
case
SNDRV_PCM_RATE_8000
:
adn
|=
0x5
<<
1
;
break
;
case
SNDRV_PCM_RATE_11025
:
adn
|=
0x4
<<
1
;
break
;
case
SNDRV_PCM_RATE_16000
:
adn
|=
0x3
<<
1
;
break
;
case
SNDRV_PCM_RATE_22050
:
adn
|=
0x2
<<
1
;
break
;
case
SNDRV_PCM_RATE_32000
:
adn
|=
0x1
<<
1
;
break
;
case
SNDRV_PCM_RATE_44100
:
case
SNDRV_PCM_RATE_48000
:
break
;
}
wm8974_write
(
codec
,
WM8974_IFACE
,
iface
);
wm8974_write
(
codec
,
WM8974_ADD
,
adn
);
return
0
;
}
static
int
wm8974_mute
(
struct
snd_soc_dai
*
dai
,
int
mute
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
u16
mute_reg
=
wm8974_read_reg_cache
(
codec
,
WM8974_DAC
)
&
0xffbf
;
if
(
mute
)
wm8974_write
(
codec
,
WM8974_DAC
,
mute_reg
|
0x40
);
else
wm8974_write
(
codec
,
WM8974_DAC
,
mute_reg
);
return
0
;
}
/* liam need to make this lower power with dapm */
static
int
wm8974_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
u16
power1
=
wm8974_read_reg_cache
(
codec
,
WM8974_POWER1
)
&
~
0x3
;
switch
(
level
)
{
case
SND_SOC_BIAS_ON
:
case
SND_SOC_BIAS_PREPARE
:
power1
|=
0x1
;
/* VMID 50k */
wm8974_write
(
codec
,
WM8974_POWER1
,
power1
);
break
;
case
SND_SOC_BIAS_STANDBY
:
power1
|=
WM8974_POWER1_BIASEN
|
WM8974_POWER1_BUFIOEN
;
if
(
codec
->
bias_level
==
SND_SOC_BIAS_OFF
)
{
/* Initial cap charge at VMID 5k */
wm8974_write
(
codec
,
WM8974_POWER1
,
power1
|
0x3
);
mdelay
(
100
);
}
power1
|=
0x2
;
/* VMID 500k */
wm8974_write
(
codec
,
WM8974_POWER1
,
power1
);
break
;
case
SND_SOC_BIAS_OFF
:
wm8974_write
(
codec
,
WM8974_POWER1
,
0
);
wm8974_write
(
codec
,
WM8974_POWER2
,
0
);
wm8974_write
(
codec
,
WM8974_POWER3
,
0
);
break
;
}
codec
->
bias_level
=
level
;
return
0
;
}
#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static
struct
snd_soc_dai_ops
wm8974_ops
=
{
.
hw_params
=
wm8974_pcm_hw_params
,
.
digital_mute
=
wm8974_mute
,
.
set_fmt
=
wm8974_set_dai_fmt
,
.
set_clkdiv
=
wm8974_set_dai_clkdiv
,
.
set_pll
=
wm8974_set_dai_pll
,
};
struct
snd_soc_dai
wm8974_dai
=
{
.
name
=
"WM8974 HiFi"
,
.
playback
=
{
.
stream_name
=
"Playback"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
/* Only 1 channel of data */
.
rates
=
WM8974_RATES
,
.
formats
=
WM8974_FORMATS
,},
.
capture
=
{
.
stream_name
=
"Capture"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
/* Only 1 channel of data */
.
rates
=
WM8974_RATES
,
.
formats
=
WM8974_FORMATS
,},
.
ops
=
&
wm8974_ops
,
.
symmetric_rates
=
1
,
};
EXPORT_SYMBOL_GPL
(
wm8974_dai
);
static
int
wm8974_suspend
(
struct
platform_device
*
pdev
,
pm_message_t
state
)
{
struct
snd_soc_device
*
socdev
=
platform_get_drvdata
(
pdev
);
struct
snd_soc_codec
*
codec
=
socdev
->
card
->
codec
;
wm8974_set_bias_level
(
codec
,
SND_SOC_BIAS_OFF
);
return
0
;
}
static
int
wm8974_resume
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_device
*
socdev
=
platform_get_drvdata
(
pdev
);
struct
snd_soc_codec
*
codec
=
socdev
->
card
->
codec
;
int
i
;
u8
data
[
2
];
u16
*
cache
=
codec
->
reg_cache
;
/* Sync reg_cache with the hardware */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
wm8974_reg
);
i
++
)
{
data
[
0
]
=
(
i
<<
1
)
|
((
cache
[
i
]
>>
8
)
&
0x0001
);
data
[
1
]
=
cache
[
i
]
&
0x00ff
;
codec
->
hw_write
(
codec
->
control_data
,
data
,
2
);
}
wm8974_set_bias_level
(
codec
,
SND_SOC_BIAS_STANDBY
);
wm8974_set_bias_level
(
codec
,
codec
->
suspend_bias_level
);
return
0
;
}
static
int
wm8974_probe
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_device
*
socdev
=
platform_get_drvdata
(
pdev
);
struct
snd_soc_codec
*
codec
;
int
ret
=
0
;
if
(
wm8974_codec
==
NULL
)
{
dev_err
(
&
pdev
->
dev
,
"Codec device not registered
\n
"
);
return
-
ENODEV
;
}
socdev
->
card
->
codec
=
wm8974_codec
;
codec
=
wm8974_codec
;
/* register pcms */
ret
=
snd_soc_new_pcms
(
socdev
,
SNDRV_DEFAULT_IDX1
,
SNDRV_DEFAULT_STR1
);
if
(
ret
<
0
)
{
dev_err
(
codec
->
dev
,
"failed to create pcms: %d
\n
"
,
ret
);
goto
pcm_err
;
}
snd_soc_add_controls
(
codec
,
wm8974_snd_controls
,
ARRAY_SIZE
(
wm8974_snd_controls
));
wm8974_add_widgets
(
codec
);
ret
=
snd_soc_init_card
(
socdev
);
if
(
ret
<
0
)
{
dev_err
(
codec
->
dev
,
"failed to register card: %d
\n
"
,
ret
);
goto
card_err
;
}
return
ret
;
card_err:
snd_soc_free_pcms
(
socdev
);
snd_soc_dapm_free
(
socdev
);
pcm_err:
return
ret
;
}
/* power down chip */
static
int
wm8974_remove
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_device
*
socdev
=
platform_get_drvdata
(
pdev
);
snd_soc_free_pcms
(
socdev
);
snd_soc_dapm_free
(
socdev
);
return
0
;
}
struct
snd_soc_codec_device
soc_codec_dev_wm8974
=
{
.
probe
=
wm8974_probe
,
.
remove
=
wm8974_remove
,
.
suspend
=
wm8974_suspend
,
.
resume
=
wm8974_resume
,
};
EXPORT_SYMBOL_GPL
(
soc_codec_dev_wm8974
);
static
__devinit
int
wm8974_register
(
struct
wm8974_priv
*
wm8974
)
{
int
ret
;
struct
snd_soc_codec
*
codec
=
&
wm8974
->
codec
;
if
(
wm8974_codec
)
{
dev_err
(
codec
->
dev
,
"Another WM8974 is registered
\n
"
);
return
-
EINVAL
;
}
mutex_init
(
&
codec
->
mutex
);
INIT_LIST_HEAD
(
&
codec
->
dapm_widgets
);
INIT_LIST_HEAD
(
&
codec
->
dapm_paths
);
codec
->
private_data
=
wm8974
;
codec
->
name
=
"WM8974"
;
codec
->
owner
=
THIS_MODULE
;
codec
->
read
=
wm8974_read_reg_cache
;
codec
->
write
=
wm8974_write
;
codec
->
bias_level
=
SND_SOC_BIAS_OFF
;
codec
->
set_bias_level
=
wm8974_set_bias_level
;
codec
->
dai
=
&
wm8974_dai
;
codec
->
num_dai
=
1
;
codec
->
reg_cache_size
=
WM8974_CACHEREGNUM
;
codec
->
reg_cache
=
&
wm8974
->
reg_cache
;
memcpy
(
codec
->
reg_cache
,
wm8974_reg
,
sizeof
(
wm8974_reg
));
ret
=
wm8974_reset
(
codec
);
if
(
ret
<
0
)
{
dev_err
(
codec
->
dev
,
"Failed to issue reset
\n
"
);
return
ret
;
}
wm8974_dai
.
dev
=
codec
->
dev
;
wm8974_set_bias_level
(
codec
,
SND_SOC_BIAS_STANDBY
);
wm8974_codec
=
codec
;
ret
=
snd_soc_register_codec
(
codec
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to register codec: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
snd_soc_register_dai
(
&
wm8974_dai
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to register DAI: %d
\n
"
,
ret
);
snd_soc_unregister_codec
(
codec
);
return
ret
;
}
return
0
;
}
static
__devexit
void
wm8974_unregister
(
struct
wm8974_priv
*
wm8974
)
{
wm8974_set_bias_level
(
&
wm8974
->
codec
,
SND_SOC_BIAS_OFF
);
snd_soc_unregister_dai
(
&
wm8974_dai
);
snd_soc_unregister_codec
(
&
wm8974
->
codec
);
kfree
(
wm8974
);
wm8974_codec
=
NULL
;
}
static
__devinit
int
wm8974_i2c_probe
(
struct
i2c_client
*
i2c
,
const
struct
i2c_device_id
*
id
)
{
struct
wm8974_priv
*
wm8974
;
struct
snd_soc_codec
*
codec
;
wm8974
=
kzalloc
(
sizeof
(
struct
wm8974_priv
),
GFP_KERNEL
);
if
(
wm8974
==
NULL
)
return
-
ENOMEM
;
codec
=
&
wm8974
->
codec
;
codec
->
hw_write
=
(
hw_write_t
)
i2c_master_send
;
i2c_set_clientdata
(
i2c
,
wm8974
);
codec
->
control_data
=
i2c
;
codec
->
dev
=
&
i2c
->
dev
;
return
wm8974_register
(
wm8974
);
}
static
__devexit
int
wm8974_i2c_remove
(
struct
i2c_client
*
client
)
{
struct
wm8974_priv
*
wm8974
=
i2c_get_clientdata
(
client
);
wm8974_unregister
(
wm8974
);
return
0
;
}
static
const
struct
i2c_device_id
wm8974_i2c_id
[]
=
{
{
"wm8974"
,
0
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
wm8974_i2c_id
);
static
struct
i2c_driver
wm8974_i2c_driver
=
{
.
driver
=
{
.
name
=
"WM8974"
,
.
owner
=
THIS_MODULE
,
},
.
probe
=
wm8974_i2c_probe
,
.
remove
=
__devexit_p
(
wm8974_i2c_remove
),
.
id_table
=
wm8974_i2c_id
,
};
static
int
__init
wm8974_modinit
(
void
)
{
return
i2c_add_driver
(
&
wm8974_i2c_driver
);
}
module_init
(
wm8974_modinit
);
static
void
__exit
wm8974_exit
(
void
)
{
i2c_del_driver
(
&
wm8974_i2c_driver
);
}
module_exit
(
wm8974_exit
);
MODULE_DESCRIPTION
(
"ASoC WM8974 driver"
);
MODULE_AUTHOR
(
"Liam Girdwood"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/codecs/wm8974.h
0 → 100644
View file @
37cfa195
/*
* wm8974.h -- WM8974 Soc Audio driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _WM8974_H
#define _WM8974_H
/* WM8974 register space */
#define WM8974_RESET 0x0
#define WM8974_POWER1 0x1
#define WM8974_POWER2 0x2
#define WM8974_POWER3 0x3
#define WM8974_IFACE 0x4
#define WM8974_COMP 0x5
#define WM8974_CLOCK 0x6
#define WM8974_ADD 0x7
#define WM8974_GPIO 0x8
#define WM8974_DAC 0xa
#define WM8974_DACVOL 0xb
#define WM8974_ADC 0xe
#define WM8974_ADCVOL 0xf
#define WM8974_EQ1 0x12
#define WM8974_EQ2 0x13
#define WM8974_EQ3 0x14
#define WM8974_EQ4 0x15
#define WM8974_EQ5 0x16
#define WM8974_DACLIM1 0x18
#define WM8974_DACLIM2 0x19
#define WM8974_NOTCH1 0x1b
#define WM8974_NOTCH2 0x1c
#define WM8974_NOTCH3 0x1d
#define WM8974_NOTCH4 0x1e
#define WM8974_ALC1 0x20
#define WM8974_ALC2 0x21
#define WM8974_ALC3 0x22
#define WM8974_NGATE 0x23
#define WM8974_PLLN 0x24
#define WM8974_PLLK1 0x25
#define WM8974_PLLK2 0x26
#define WM8974_PLLK3 0x27
#define WM8974_ATTEN 0x28
#define WM8974_INPUT 0x2c
#define WM8974_INPPGA 0x2d
#define WM8974_ADCBOOST 0x2f
#define WM8974_OUTPUT 0x31
#define WM8974_SPKMIX 0x32
#define WM8974_SPKVOL 0x36
#define WM8974_MONOMIX 0x38
#define WM8974_CACHEREGNUM 57
/* Clock divider Id's */
#define WM8974_OPCLKDIV 0
#define WM8974_MCLKDIV 1
#define WM8974_ADCCLK 2
#define WM8974_DACCLK 3
#define WM8974_BCLKDIV 4
/* DAC clock dividers */
#define WM8974_DACCLK_F2 (1 << 3)
#define WM8974_DACCLK_F4 (0 << 3)
/* ADC clock dividers */
#define WM8974_ADCCLK_F2 (1 << 3)
#define WM8974_ADCCLK_F4 (0 << 3)
/* PLL Out dividers */
#define WM8974_OPCLKDIV_1 (0 << 4)
#define WM8974_OPCLKDIV_2 (1 << 4)
#define WM8974_OPCLKDIV_3 (2 << 4)
#define WM8974_OPCLKDIV_4 (3 << 4)
/* BCLK clock dividers */
#define WM8974_BCLKDIV_1 (0 << 2)
#define WM8974_BCLKDIV_2 (1 << 2)
#define WM8974_BCLKDIV_4 (2 << 2)
#define WM8974_BCLKDIV_8 (3 << 2)
#define WM8974_BCLKDIV_16 (4 << 2)
#define WM8974_BCLKDIV_32 (5 << 2)
/* MCLK clock dividers */
#define WM8974_MCLKDIV_1 (0 << 5)
#define WM8974_MCLKDIV_1_5 (1 << 5)
#define WM8974_MCLKDIV_2 (2 << 5)
#define WM8974_MCLKDIV_3 (3 << 5)
#define WM8974_MCLKDIV_4 (4 << 5)
#define WM8974_MCLKDIV_6 (5 << 5)
#define WM8974_MCLKDIV_8 (6 << 5)
#define WM8974_MCLKDIV_12 (7 << 5)
extern
struct
snd_soc_dai
wm8974_dai
;
extern
struct
snd_soc_codec_device
soc_codec_dev_wm8974
;
#endif
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