Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
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
Hide 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