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
7034ef5f
Commit
7034ef5f
authored
Oct 26, 2015
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branches 'asoc/topic/atmel-classd' and 'asoc/topic/da7213' into asoc-next
parents
2c218b74
391ac3ef
955da485
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1083 additions
and
21 deletions
+1083
-21
Documentation/devicetree/bindings/sound/atmel-classd.txt
Documentation/devicetree/bindings/sound/atmel-classd.txt
+52
-0
Documentation/devicetree/bindings/sound/da7213.txt
Documentation/devicetree/bindings/sound/da7213.txt
+41
-0
include/sound/da7213.h
include/sound/da7213.h
+0
-3
sound/soc/atmel/Kconfig
sound/soc/atmel/Kconfig
+9
-0
sound/soc/atmel/Makefile
sound/soc/atmel/Makefile
+2
-0
sound/soc/atmel/atmel-classd.c
sound/soc/atmel/atmel-classd.c
+679
-0
sound/soc/atmel/atmel-classd.h
sound/soc/atmel/atmel-classd.h
+120
-0
sound/soc/codecs/da7213.c
sound/soc/codecs/da7213.c
+174
-16
sound/soc/codecs/da7213.h
sound/soc/codecs/da7213.h
+6
-2
No files found.
Documentation/devicetree/bindings/sound/atmel-classd.txt
0 → 100644
View file @
7034ef5f
* Atmel ClassD driver under ALSA SoC architecture
Required properties:
- compatible
Should be "atmel,sama5d2-classd".
- reg
Should contain ClassD registers location and length.
- interrupts
Should contain the IRQ line for the ClassD.
- dmas
One DMA specifiers as described in atmel-dma.txt and dma.txt files.
- dma-names
Must be "tx".
- clock-names
Tuple listing input clock names.
Required elements: "pclk", "gclk" and "aclk".
- clocks
Please refer to clock-bindings.txt.
Optional properties:
- pinctrl-names, pinctrl-0
Please refer to pinctrl-bindings.txt.
- atmel,model
The user-visible name of this sound complex.
The default value is "CLASSD".
- atmel,pwm-type
PWM modulation type, "single" or "diff".
The default value is "single".
- atmel,non-overlap-time
Set non-overlapping time, the unit is nanosecond(ns).
There are four values,
<5>, <10>, <15>, <20>, the default value is <10>.
Non-overlapping will be disabled if not specified.
Example:
classd: classd@fc048000 {
compatible = "atmel,sama5d2-classd";
reg = <0xfc048000 0x100>;
interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>;
dmas = <&dma0
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
| AT91_XDMAC_DT_PERID(47))>;
dma-names = "tx";
clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
clock-names = "pclk", "gclk", "aclk";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_classd_default>;
atmel,model = "classd @ SAMA5D2-Xplained";
atmel,pwm-type = "diff";
atmel,non-overlap-time = <10>;
};
Documentation/devicetree/bindings/sound/da7213.txt
0 → 100644
View file @
7034ef5f
Dialog Semiconductor DA7213 Audio Codec bindings
======
Required properties:
- compatible : Should be "dlg,da7213"
- reg: Specifies the I2C slave address
Optional properties:
- clocks : phandle and clock specifier for codec MCLK.
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
- dlg,micbias1-lvl : Voltage (mV) for Mic Bias 1
[<1600>, <2200>, <2500>, <3000>]
- dlg,micbias2-lvl : Voltage (mV) for Mic Bias 2
[<1600>, <2200>, <2500>, <3000>]
- dlg,dmic-data-sel : DMIC channel select based on clock edge.
["lrise_rfall", "lfall_rrise"]
- dlg,dmic-samplephase : When to sample audio from DMIC.
["on_clkedge", "between_clkedge"]
- dlg,dmic-clkrate : DMIC clock frequency (Hz).
[<1500000>, <3000000>]
======
Example:
codec_i2c: da7213@1a {
compatible = "dlg,da7213";
reg = <0x1a>;
clocks = <&clks 201>;
clock-names = "mclk";
dlg,micbias1-lvl = <2500>;
dlg,micbias2-lvl = <2500>;
dlg,dmic-data-sel = "lrise_rfall";
dlg,dmic-samplephase = "between_clkedge";
dlg,dmic-clkrate = <3000000>;
};
include/sound/da7213.h
View file @
7034ef5f
...
...
@@ -44,9 +44,6 @@ struct da7213_platform_data {
enum
da7213_dmic_data_sel
dmic_data_sel
;
enum
da7213_dmic_samplephase
dmic_samplephase
;
enum
da7213_dmic_clk_rate
dmic_clk_rate
;
/* MCLK squaring config */
bool
mclk_squaring
;
};
#endif
/* _DA7213_PDATA_H */
sound/soc/atmel/Kconfig
View file @
7034ef5f
...
...
@@ -59,4 +59,13 @@ config SND_AT91_SOC_SAM9X5_WM8731
help
Say Y if you want to add support for audio SoC on an
at91sam9x5 based board that is using WM8731 codec.
config SND_ATMEL_SOC_CLASSD
tristate "Atmel ASoC driver for boards using CLASSD"
depends on ARCH_AT91 || COMPILE_TEST
select SND_ATMEL_SOC_DMA
select REGMAP_MMIO
help
Say Y if you want to add support for Atmel ASoC driver for boards using
CLASSD.
endif
sound/soc/atmel/Makefile
View file @
7034ef5f
...
...
@@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
snd-soc-sam9g20-wm8731-objs
:=
sam9g20_wm8731.o
snd-atmel-soc-wm8904-objs
:=
atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs
:=
sam9x5_wm8731.o
snd-atmel-soc-classd-objs
:=
atmel-classd.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_AT91_SOC_SAM9X5_WM8731)
+=
snd-soc-sam9x5-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD)
+=
snd-atmel-soc-classd.o
sound/soc/atmel/atmel-classd.c
0 → 100644
View file @
7034ef5f
/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
*
* Copyright (C) 2015 Atmel
*
* Author: Songjun Wu <songjun.wu@atmel.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 or later
* as published by the Free Software Foundation.
*/
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "atmel-classd.h"
struct
atmel_classd_pdata
{
bool
non_overlap_enable
;
int
non_overlap_time
;
int
pwm_type
;
const
char
*
card_name
;
};
struct
atmel_classd
{
dma_addr_t
phy_base
;
struct
regmap
*
regmap
;
struct
clk
*
pclk
;
struct
clk
*
gclk
;
struct
clk
*
aclk
;
int
irq
;
const
struct
atmel_classd_pdata
*
pdata
;
};
#ifdef CONFIG_OF
static
const
struct
of_device_id
atmel_classd_of_match
[]
=
{
{
.
compatible
=
"atmel,sama5d2-classd"
,
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
atmel_classd_of_match
);
static
struct
atmel_classd_pdata
*
atmel_classd_dt_init
(
struct
device
*
dev
)
{
struct
device_node
*
np
=
dev
->
of_node
;
struct
atmel_classd_pdata
*
pdata
;
const
char
*
pwm_type
;
int
ret
;
if
(
!
np
)
{
dev_err
(
dev
,
"device node not found
\n
"
);
return
ERR_PTR
(
-
EINVAL
);
}
pdata
=
devm_kzalloc
(
dev
,
sizeof
(
*
pdata
),
GFP_KERNEL
);
if
(
!
pdata
)
return
ERR_PTR
(
-
ENOMEM
);
ret
=
of_property_read_string
(
np
,
"atmel,pwm-type"
,
&
pwm_type
);
if
((
ret
==
0
)
&&
(
strcmp
(
pwm_type
,
"diff"
)
==
0
))
pdata
->
pwm_type
=
CLASSD_MR_PWMTYP_DIFF
;
else
pdata
->
pwm_type
=
CLASSD_MR_PWMTYP_SINGLE
;
ret
=
of_property_read_u32
(
np
,
"atmel,non-overlap-time"
,
&
pdata
->
non_overlap_time
);
if
(
ret
)
pdata
->
non_overlap_enable
=
false
;
else
pdata
->
non_overlap_enable
=
true
;
ret
=
of_property_read_string
(
np
,
"atmel,model"
,
&
pdata
->
card_name
);
if
(
ret
)
pdata
->
card_name
=
"CLASSD"
;
return
pdata
;
}
#else
static
inline
struct
atmel_classd_pdata
*
atmel_classd_dt_init
(
struct
device
*
dev
)
{
return
ERR_PTR
(
-
EINVAL
);
}
#endif
#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
| SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \
| SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \
| SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \
| SNDRV_PCM_RATE_96000)
static
const
struct
snd_pcm_hardware
atmel_classd_hw
=
{
.
info
=
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_RESUME
|
SNDRV_PCM_INFO_PAUSE
,
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
ATMEL_CLASSD_RATES
,
.
rate_min
=
8000
,
.
rate_max
=
96000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
64
*
1024
,
.
period_bytes_min
=
256
,
.
period_bytes_max
=
32
*
1024
,
.
periods_min
=
2
,
.
periods_max
=
256
,
};
#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024)
/* cpu dai component */
static
int
atmel_classd_cpu_dai_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
cpu_dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
regmap_write
(
dd
->
regmap
,
CLASSD_THR
,
0x0
);
return
clk_prepare_enable
(
dd
->
pclk
);
}
static
void
atmel_classd_cpu_dai_shutdown
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
cpu_dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
clk_disable_unprepare
(
dd
->
pclk
);
}
static
const
struct
snd_soc_dai_ops
atmel_classd_cpu_dai_ops
=
{
.
startup
=
atmel_classd_cpu_dai_startup
,
.
shutdown
=
atmel_classd_cpu_dai_shutdown
,
};
static
struct
snd_soc_dai_driver
atmel_classd_cpu_dai
=
{
.
playback
=
{
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
ATMEL_CLASSD_RATES
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,},
.
ops
=
&
atmel_classd_cpu_dai_ops
,
};
static
const
struct
snd_soc_component_driver
atmel_classd_cpu_dai_component
=
{
.
name
=
"atmel-classd"
,
};
/* platform */
static
int
atmel_classd_platform_configure_dma
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
struct
dma_slave_config
*
slave_config
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
if
(
params_physical_width
(
params
)
!=
16
)
{
dev_err
(
rtd
->
platform
->
dev
,
"only supports 16-bit audio data
\n
"
);
return
-
EINVAL
;
}
slave_config
->
direction
=
DMA_MEM_TO_DEV
;
slave_config
->
dst_addr
=
dd
->
phy_base
+
CLASSD_THR
;
slave_config
->
dst_addr_width
=
DMA_SLAVE_BUSWIDTH_4_BYTES
;
slave_config
->
dst_maxburst
=
1
;
slave_config
->
src_maxburst
=
1
;
slave_config
->
device_fc
=
false
;
return
0
;
}
static
const
struct
snd_dmaengine_pcm_config
atmel_classd_dmaengine_pcm_config
=
{
.
prepare_slave_config
=
atmel_classd_platform_configure_dma
,
.
pcm_hardware
=
&
atmel_classd_hw
,
.
prealloc_buffer_size
=
ATMEL_CLASSD_PREALLOC_BUF_SIZE
,
};
/* codec */
static
const
char
*
const
mono_mode_text
[]
=
{
"mix"
,
"sat"
,
"left"
,
"right"
};
static
SOC_ENUM_SINGLE_DECL
(
classd_mono_mode_enum
,
CLASSD_INTPMR
,
CLASSD_INTPMR_MONO_MODE_SHIFT
,
mono_mode_text
);
static
const
char
*
const
eqcfg_text
[]
=
{
"Treble-12dB"
,
"Treble-6dB"
,
"Medium-8dB"
,
"Medium-3dB"
,
"Bass-12dB"
,
"Bass-6dB"
,
"0 dB"
,
"Bass+6dB"
,
"Bass+12dB"
,
"Medium+3dB"
,
"Medium+8dB"
,
"Treble+6dB"
,
"Treble+12dB"
,
};
static
const
unsigned
int
eqcfg_value
[]
=
{
CLASSD_INTPMR_EQCFG_T_CUT_12
,
CLASSD_INTPMR_EQCFG_T_CUT_6
,
CLASSD_INTPMR_EQCFG_M_CUT_8
,
CLASSD_INTPMR_EQCFG_M_CUT_3
,
CLASSD_INTPMR_EQCFG_B_CUT_12
,
CLASSD_INTPMR_EQCFG_B_CUT_6
,
CLASSD_INTPMR_EQCFG_FLAT
,
CLASSD_INTPMR_EQCFG_B_BOOST_6
,
CLASSD_INTPMR_EQCFG_B_BOOST_12
,
CLASSD_INTPMR_EQCFG_M_BOOST_3
,
CLASSD_INTPMR_EQCFG_M_BOOST_8
,
CLASSD_INTPMR_EQCFG_T_BOOST_6
,
CLASSD_INTPMR_EQCFG_T_BOOST_12
,
};
static
SOC_VALUE_ENUM_SINGLE_DECL
(
classd_eqcfg_enum
,
CLASSD_INTPMR
,
CLASSD_INTPMR_EQCFG_SHIFT
,
0xf
,
eqcfg_text
,
eqcfg_value
);
static
const
DECLARE_TLV_DB_SCALE
(
classd_digital_tlv
,
-
7800
,
100
,
1
);
static
const
struct
snd_kcontrol_new
atmel_classd_snd_controls
[]
=
{
SOC_DOUBLE_TLV
(
"Playback Volume"
,
CLASSD_INTPMR
,
CLASSD_INTPMR_ATTL_SHIFT
,
CLASSD_INTPMR_ATTR_SHIFT
,
78
,
1
,
classd_digital_tlv
),
SOC_SINGLE
(
"Deemphasis Switch"
,
CLASSD_INTPMR
,
CLASSD_INTPMR_DEEMP_SHIFT
,
1
,
0
),
SOC_SINGLE
(
"Mono Switch"
,
CLASSD_INTPMR
,
CLASSD_INTPMR_MONO_SHIFT
,
1
,
0
),
SOC_SINGLE
(
"Swap Switch"
,
CLASSD_INTPMR
,
CLASSD_INTPMR_SWAP_SHIFT
,
1
,
0
),
SOC_ENUM
(
"Mono Mode"
,
classd_mono_mode_enum
),
SOC_ENUM
(
"EQ"
,
classd_eqcfg_enum
),
};
static
const
char
*
const
pwm_type
[]
=
{
"Single ended"
,
"Differential"
};
static
int
atmel_classd_codec_probe
(
struct
snd_soc_codec
*
codec
)
{
struct
snd_soc_card
*
card
=
snd_soc_codec_get_drvdata
(
codec
);
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
card
);
const
struct
atmel_classd_pdata
*
pdata
=
dd
->
pdata
;
u32
mask
,
val
;
mask
=
CLASSD_MR_PWMTYP_MASK
;
val
=
pdata
->
pwm_type
<<
CLASSD_MR_PWMTYP_SHIFT
;
mask
|=
CLASSD_MR_NON_OVERLAP_MASK
;
if
(
pdata
->
non_overlap_enable
)
{
val
|=
(
CLASSD_MR_NON_OVERLAP_EN
<<
CLASSD_MR_NON_OVERLAP_SHIFT
);
mask
|=
CLASSD_MR_NOVR_VAL_MASK
;
switch
(
pdata
->
non_overlap_time
)
{
case
5
:
val
|=
(
CLASSD_MR_NOVR_VAL_5NS
<<
CLASSD_MR_NOVR_VAL_SHIFT
);
break
;
case
10
:
val
|=
(
CLASSD_MR_NOVR_VAL_10NS
<<
CLASSD_MR_NOVR_VAL_SHIFT
);
break
;
case
15
:
val
|=
(
CLASSD_MR_NOVR_VAL_15NS
<<
CLASSD_MR_NOVR_VAL_SHIFT
);
break
;
case
20
:
val
|=
(
CLASSD_MR_NOVR_VAL_20NS
<<
CLASSD_MR_NOVR_VAL_SHIFT
);
break
;
default:
val
|=
(
CLASSD_MR_NOVR_VAL_10NS
<<
CLASSD_MR_NOVR_VAL_SHIFT
);
dev_warn
(
codec
->
dev
,
"non-overlapping value %d is invalid, the default value 10 is specified
\n
"
,
pdata
->
non_overlap_time
);
break
;
}
}
snd_soc_update_bits
(
codec
,
CLASSD_MR
,
mask
,
val
);
dev_info
(
codec
->
dev
,
"PWM modulation type is %s, non-overlapping is %s
\n
"
,
pwm_type
[
pdata
->
pwm_type
],
pdata
->
non_overlap_enable
?
"enabled"
:
"disabled"
);
return
0
;
}
static
struct
regmap
*
atmel_classd_codec_get_remap
(
struct
device
*
dev
)
{
return
dev_get_regmap
(
dev
,
NULL
);
}
static
struct
snd_soc_codec_driver
soc_codec_dev_classd
=
{
.
probe
=
atmel_classd_codec_probe
,
.
controls
=
atmel_classd_snd_controls
,
.
num_controls
=
ARRAY_SIZE
(
atmel_classd_snd_controls
),
.
get_regmap
=
atmel_classd_codec_get_remap
,
};
/* codec dai component */
static
int
atmel_classd_codec_dai_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
codec_dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
int
ret
;
ret
=
clk_prepare_enable
(
dd
->
aclk
);
if
(
ret
)
return
ret
;
return
clk_prepare_enable
(
dd
->
gclk
);
}
static
int
atmel_classd_codec_dai_digital_mute
(
struct
snd_soc_dai
*
codec_dai
,
int
mute
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
u32
mask
,
val
;
mask
=
CLASSD_MR_LMUTE_MASK
|
CLASSD_MR_RMUTE_MASK
;
if
(
mute
)
val
=
mask
;
else
val
=
0
;
snd_soc_update_bits
(
codec
,
CLASSD_MR
,
mask
,
val
);
return
0
;
}
#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
static
struct
{
int
rate
;
int
sample_rate
;
int
dsp_clk
;
unsigned
long
aclk_rate
;
}
const
sample_rates
[]
=
{
{
8000
,
CLASSD_INTPMR_FRAME_8K
,
CLASSD_INTPMR_DSP_CLK_FREQ_12M288
,
CLASSD_ACLK_RATE_12M288_MPY_8
},
{
16000
,
CLASSD_INTPMR_FRAME_16K
,
CLASSD_INTPMR_DSP_CLK_FREQ_12M288
,
CLASSD_ACLK_RATE_12M288_MPY_8
},
{
32000
,
CLASSD_INTPMR_FRAME_32K
,
CLASSD_INTPMR_DSP_CLK_FREQ_12M288
,
CLASSD_ACLK_RATE_12M288_MPY_8
},
{
48000
,
CLASSD_INTPMR_FRAME_48K
,
CLASSD_INTPMR_DSP_CLK_FREQ_12M288
,
CLASSD_ACLK_RATE_12M288_MPY_8
},
{
96000
,
CLASSD_INTPMR_FRAME_96K
,
CLASSD_INTPMR_DSP_CLK_FREQ_12M288
,
CLASSD_ACLK_RATE_12M288_MPY_8
},
{
22050
,
CLASSD_INTPMR_FRAME_22K
,
CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
,
CLASSD_ACLK_RATE_11M2896_MPY_8
},
{
44100
,
CLASSD_INTPMR_FRAME_44K
,
CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
,
CLASSD_ACLK_RATE_11M2896_MPY_8
},
{
88200
,
CLASSD_INTPMR_FRAME_88K
,
CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
,
CLASSD_ACLK_RATE_11M2896_MPY_8
},
};
static
int
atmel_classd_codec_dai_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
codec_dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
int
fs
;
int
i
,
best
,
best_val
,
cur_val
,
ret
;
u32
mask
,
val
;
fs
=
params_rate
(
params
);
best
=
0
;
best_val
=
abs
(
fs
-
sample_rates
[
0
].
rate
);
for
(
i
=
1
;
i
<
ARRAY_SIZE
(
sample_rates
);
i
++
)
{
/* Closest match */
cur_val
=
abs
(
fs
-
sample_rates
[
i
].
rate
);
if
(
cur_val
<
best_val
)
{
best
=
i
;
best_val
=
cur_val
;
}
}
dev_dbg
(
codec
->
dev
,
"Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz
\n
"
,
sample_rates
[
best
].
rate
,
sample_rates
[
best
].
aclk_rate
);
clk_disable_unprepare
(
dd
->
gclk
);
clk_disable_unprepare
(
dd
->
aclk
);
ret
=
clk_set_rate
(
dd
->
aclk
,
sample_rates
[
best
].
aclk_rate
);
if
(
ret
)
return
ret
;
mask
=
CLASSD_INTPMR_DSP_CLK_FREQ_MASK
|
CLASSD_INTPMR_FRAME_MASK
;
val
=
(
sample_rates
[
best
].
dsp_clk
<<
CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT
)
|
(
sample_rates
[
best
].
sample_rate
<<
CLASSD_INTPMR_FRAME_SHIFT
);
snd_soc_update_bits
(
codec
,
CLASSD_INTPMR
,
mask
,
val
);
ret
=
clk_prepare_enable
(
dd
->
aclk
);
if
(
ret
)
return
ret
;
return
clk_prepare_enable
(
dd
->
gclk
);
}
static
void
atmel_classd_codec_dai_shutdown
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
codec_dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
clk_disable_unprepare
(
dd
->
gclk
);
clk_disable_unprepare
(
dd
->
aclk
);
}
static
int
atmel_classd_codec_dai_prepare
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
codec_dai
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
snd_soc_update_bits
(
codec
,
CLASSD_MR
,
CLASSD_MR_LEN_MASK
|
CLASSD_MR_REN_MASK
,
(
CLASSD_MR_LEN_DIS
<<
CLASSD_MR_LEN_SHIFT
)
|
(
CLASSD_MR_REN_DIS
<<
CLASSD_MR_REN_SHIFT
));
return
0
;
}
static
int
atmel_classd_codec_dai_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
,
struct
snd_soc_dai
*
codec_dai
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
u32
mask
,
val
;
mask
=
CLASSD_MR_LEN_MASK
|
CLASSD_MR_REN_MASK
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
case
SNDRV_PCM_TRIGGER_RESUME
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
val
=
mask
;
break
;
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
val
=
(
CLASSD_MR_LEN_DIS
<<
CLASSD_MR_LEN_SHIFT
)
|
(
CLASSD_MR_REN_DIS
<<
CLASSD_MR_REN_SHIFT
);
break
;
default:
return
-
EINVAL
;
}
snd_soc_update_bits
(
codec
,
CLASSD_MR
,
mask
,
val
);
return
0
;
}
static
const
struct
snd_soc_dai_ops
atmel_classd_codec_dai_ops
=
{
.
digital_mute
=
atmel_classd_codec_dai_digital_mute
,
.
startup
=
atmel_classd_codec_dai_startup
,
.
shutdown
=
atmel_classd_codec_dai_shutdown
,
.
hw_params
=
atmel_classd_codec_dai_hw_params
,
.
prepare
=
atmel_classd_codec_dai_prepare
,
.
trigger
=
atmel_classd_codec_dai_trigger
,
};
#define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
static
struct
snd_soc_dai_driver
atmel_classd_codec_dai
=
{
.
name
=
ATMEL_CLASSD_CODEC_DAI_NAME
,
.
playback
=
{
.
stream_name
=
"Playback"
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
ATMEL_CLASSD_RATES
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
},
.
ops
=
&
atmel_classd_codec_dai_ops
,
};
/* ASoC sound card */
static
int
atmel_classd_asoc_card_init
(
struct
device
*
dev
,
struct
snd_soc_card
*
card
)
{
struct
snd_soc_dai_link
*
dai_link
;
struct
atmel_classd
*
dd
=
snd_soc_card_get_drvdata
(
card
);
dai_link
=
devm_kzalloc
(
dev
,
sizeof
(
*
dai_link
),
GFP_KERNEL
);
if
(
!
dai_link
)
return
-
ENOMEM
;
dai_link
->
name
=
"CLASSD"
;
dai_link
->
stream_name
=
"CLASSD PCM"
;
dai_link
->
codec_dai_name
=
ATMEL_CLASSD_CODEC_DAI_NAME
;
dai_link
->
cpu_dai_name
=
dev_name
(
dev
);
dai_link
->
codec_name
=
dev_name
(
dev
);
dai_link
->
platform_name
=
dev_name
(
dev
);
card
->
dai_link
=
dai_link
;
card
->
num_links
=
1
;
card
->
name
=
dd
->
pdata
->
card_name
;
card
->
dev
=
dev
;
return
0
;
};
/* regmap configuration */
static
const
struct
reg_default
atmel_classd_reg_defaults
[]
=
{
{
CLASSD_INTPMR
,
0x00301212
},
};
#define ATMEL_CLASSD_REG_MAX 0xE4
static
const
struct
regmap_config
atmel_classd_regmap_config
=
{
.
reg_bits
=
32
,
.
reg_stride
=
4
,
.
val_bits
=
32
,
.
max_register
=
ATMEL_CLASSD_REG_MAX
,
.
cache_type
=
REGCACHE_FLAT
,
.
reg_defaults
=
atmel_classd_reg_defaults
,
.
num_reg_defaults
=
ARRAY_SIZE
(
atmel_classd_reg_defaults
),
};
static
int
atmel_classd_probe
(
struct
platform_device
*
pdev
)
{
struct
device
*
dev
=
&
pdev
->
dev
;
struct
atmel_classd
*
dd
;
struct
resource
*
res
;
void
__iomem
*
io_base
;
const
struct
atmel_classd_pdata
*
pdata
;
struct
snd_soc_card
*
card
;
int
ret
;
pdata
=
dev_get_platdata
(
dev
);
if
(
!
pdata
)
{
pdata
=
atmel_classd_dt_init
(
dev
);
if
(
IS_ERR
(
pdata
))
return
PTR_ERR
(
pdata
);
}
dd
=
devm_kzalloc
(
dev
,
sizeof
(
*
dd
),
GFP_KERNEL
);
if
(
!
dd
)
return
-
ENOMEM
;
dd
->
pdata
=
pdata
;
dd
->
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
dd
->
irq
<
0
)
{
ret
=
dd
->
irq
;
dev_err
(
dev
,
"failed to could not get irq: %d
\n
"
,
ret
);
return
ret
;
}
dd
->
pclk
=
devm_clk_get
(
dev
,
"pclk"
);
if
(
IS_ERR
(
dd
->
pclk
))
{
ret
=
PTR_ERR
(
dd
->
pclk
);
dev_err
(
dev
,
"failed to get peripheral clock: %d
\n
"
,
ret
);
return
ret
;
}
dd
->
gclk
=
devm_clk_get
(
dev
,
"gclk"
);
if
(
IS_ERR
(
dd
->
gclk
))
{
ret
=
PTR_ERR
(
dd
->
gclk
);
dev_err
(
dev
,
"failed to get GCK clock: %d
\n
"
,
ret
);
return
ret
;
}
dd
->
aclk
=
devm_clk_get
(
dev
,
"aclk"
);
if
(
IS_ERR
(
dd
->
aclk
))
{
ret
=
PTR_ERR
(
dd
->
aclk
);
dev_err
(
dev
,
"failed to get audio clock: %d
\n
"
,
ret
);
return
ret
;
}
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
res
)
{
dev_err
(
dev
,
"no memory resource
\n
"
);
return
-
ENXIO
;
}
io_base
=
devm_ioremap_resource
(
dev
,
res
);
if
(
IS_ERR
(
io_base
))
{
ret
=
PTR_ERR
(
io_base
);
dev_err
(
dev
,
"failed to remap register memory: %d
\n
"
,
ret
);
return
ret
;
}
dd
->
phy_base
=
res
->
start
;
dd
->
regmap
=
devm_regmap_init_mmio
(
dev
,
io_base
,
&
atmel_classd_regmap_config
);
if
(
IS_ERR
(
dd
->
regmap
))
{
ret
=
PTR_ERR
(
dd
->
regmap
);
dev_err
(
dev
,
"failed to init register map: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
devm_snd_soc_register_component
(
dev
,
&
atmel_classd_cpu_dai_component
,
&
atmel_classd_cpu_dai
,
1
);
if
(
ret
)
{
dev_err
(
dev
,
"could not register CPU DAI: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
devm_snd_dmaengine_pcm_register
(
dev
,
&
atmel_classd_dmaengine_pcm_config
,
0
);
if
(
ret
)
{
dev_err
(
dev
,
"could not register platform: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
snd_soc_register_codec
(
dev
,
&
soc_codec_dev_classd
,
&
atmel_classd_codec_dai
,
1
);
if
(
ret
)
{
dev_err
(
dev
,
"could not register codec: %d
\n
"
,
ret
);
return
ret
;
}
/* register sound card */
card
=
devm_kzalloc
(
dev
,
sizeof
(
*
card
),
GFP_KERNEL
);
if
(
!
card
)
return
-
ENOMEM
;
snd_soc_card_set_drvdata
(
card
,
dd
);
platform_set_drvdata
(
pdev
,
card
);
ret
=
atmel_classd_asoc_card_init
(
dev
,
card
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to init sound card
\n
"
);
return
ret
;
}
ret
=
devm_snd_soc_register_card
(
dev
,
card
);
if
(
ret
)
{
dev_err
(
dev
,
"failed to register sound card: %d
\n
"
,
ret
);
return
ret
;
}
return
0
;
}
static
int
atmel_classd_remove
(
struct
platform_device
*
pdev
)
{
snd_soc_unregister_codec
(
&
pdev
->
dev
);
return
0
;
}
static
struct
platform_driver
atmel_classd_driver
=
{
.
driver
=
{
.
name
=
"atmel-classd"
,
.
of_match_table
=
of_match_ptr
(
atmel_classd_of_match
),
.
pm
=
&
snd_soc_pm_ops
,
},
.
probe
=
atmel_classd_probe
,
.
remove
=
atmel_classd_remove
,
};
module_platform_driver
(
atmel_classd_driver
);
MODULE_DESCRIPTION
(
"Atmel ClassD driver under ALSA SoC architecture"
);
MODULE_AUTHOR
(
"Songjun Wu <songjun.wu@atmel.com>"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/atmel/atmel-classd.h
0 → 100644
View file @
7034ef5f
#ifndef __ATMEL_CLASSD_H_
#define __ATMEL_CLASSD_H_
#define CLASSD_CR 0x00000000
#define CLASSD_CR_RESET 0x1
#define CLASSD_MR 0x00000004
#define CLASSD_MR_LEN_DIS 0x0
#define CLASSD_MR_LEN_EN 0x1
#define CLASSD_MR_LEN_MASK (0x1 << 0)
#define CLASSD_MR_LEN_SHIFT (0)
#define CLASSD_MR_LMUTE_DIS 0x0
#define CLASSD_MR_LMUTE_EN 0x1
#define CLASSD_MR_LMUTE_SHIFT (0x1)
#define CLASSD_MR_LMUTE_MASK (0x1 << 1)
#define CLASSD_MR_REN_DIS 0x0
#define CLASSD_MR_REN_EN 0x1
#define CLASSD_MR_REN_MASK (0x1 << 4)
#define CLASSD_MR_REN_SHIFT (4)
#define CLASSD_MR_RMUTE_DIS 0x0
#define CLASSD_MR_RMUTE_EN 0x1
#define CLASSD_MR_RMUTE_SHIFT (0x5)
#define CLASSD_MR_RMUTE_MASK (0x1 << 5)
#define CLASSD_MR_PWMTYP_SINGLE 0x0
#define CLASSD_MR_PWMTYP_DIFF 0x1
#define CLASSD_MR_PWMTYP_MASK (0x1 << 8)
#define CLASSD_MR_PWMTYP_SHIFT (8)
#define CLASSD_MR_NON_OVERLAP_DIS 0x0
#define CLASSD_MR_NON_OVERLAP_EN 0x1
#define CLASSD_MR_NON_OVERLAP_MASK (0x1 << 16)
#define CLASSD_MR_NON_OVERLAP_SHIFT (16)
#define CLASSD_MR_NOVR_VAL_5NS 0x0
#define CLASSD_MR_NOVR_VAL_10NS 0x1
#define CLASSD_MR_NOVR_VAL_15NS 0x2
#define CLASSD_MR_NOVR_VAL_20NS 0x3
#define CLASSD_MR_NOVR_VAL_MASK (0x3 << 20)
#define CLASSD_MR_NOVR_VAL_SHIFT (20)
#define CLASSD_INTPMR 0x00000008
#define CLASSD_INTPMR_ATTL_MASK (0x3f << 0)
#define CLASSD_INTPMR_ATTL_SHIFT (0)
#define CLASSD_INTPMR_ATTR_MASK (0x3f << 8)
#define CLASSD_INTPMR_ATTR_SHIFT (8)
#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288 0x0
#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896 0x1
#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK (0x1 << 16)
#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT (16)
#define CLASSD_INTPMR_DEEMP_DIS 0x0
#define CLASSD_INTPMR_DEEMP_EN 0x1
#define CLASSD_INTPMR_DEEMP_MASK (0x1 << 18)
#define CLASSD_INTPMR_DEEMP_SHIFT (18)
#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB 0x0
#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB 0x1
#define CLASSD_INTPMR_SWAP_MASK (0x1 << 19)
#define CLASSD_INTPMR_SWAP_SHIFT (19)
#define CLASSD_INTPMR_FRAME_8K 0x0
#define CLASSD_INTPMR_FRAME_16K 0x1
#define CLASSD_INTPMR_FRAME_32K 0x2
#define CLASSD_INTPMR_FRAME_48K 0x3
#define CLASSD_INTPMR_FRAME_96K 0x4
#define CLASSD_INTPMR_FRAME_22K 0x5
#define CLASSD_INTPMR_FRAME_44K 0x6
#define CLASSD_INTPMR_FRAME_88K 0x7
#define CLASSD_INTPMR_FRAME_MASK (0x7 << 20)
#define CLASSD_INTPMR_FRAME_SHIFT (20)
#define CLASSD_INTPMR_EQCFG_FLAT 0x0
#define CLASSD_INTPMR_EQCFG_B_BOOST_12 0x1
#define CLASSD_INTPMR_EQCFG_B_BOOST_6 0x2
#define CLASSD_INTPMR_EQCFG_B_CUT_12 0x3
#define CLASSD_INTPMR_EQCFG_B_CUT_6 0x4
#define CLASSD_INTPMR_EQCFG_M_BOOST_3 0x5
#define CLASSD_INTPMR_EQCFG_M_BOOST_8 0x6
#define CLASSD_INTPMR_EQCFG_M_CUT_3 0x7
#define CLASSD_INTPMR_EQCFG_M_CUT_8 0x8
#define CLASSD_INTPMR_EQCFG_T_BOOST_12 0x9
#define CLASSD_INTPMR_EQCFG_T_BOOST_6 0xa
#define CLASSD_INTPMR_EQCFG_T_CUT_12 0xb
#define CLASSD_INTPMR_EQCFG_T_CUT_6 0xc
#define CLASSD_INTPMR_EQCFG_SHIFT (24)
#define CLASSD_INTPMR_MONO_DIS 0x0
#define CLASSD_INTPMR_MONO_EN 0x1
#define CLASSD_INTPMR_MONO_MASK (0x1 << 28)
#define CLASSD_INTPMR_MONO_SHIFT (28)
#define CLASSD_INTPMR_MONO_MODE_MIX 0x0
#define CLASSD_INTPMR_MONO_MODE_SAT 0x1
#define CLASSD_INTPMR_MONO_MODE_LEFT 0x2
#define CLASSD_INTPMR_MONO_MODE_RIGHT 0x3
#define CLASSD_INTPMR_MONO_MODE_MASK (0x3 << 29)
#define CLASSD_INTPMR_MONO_MODE_SHIFT (29)
#define CLASSD_INTSR 0x0000000c
#define CLASSD_THR 0x00000010
#define CLASSD_IER 0x00000014
#define CLASSD_IDR 0x00000018
#define CLASSD_IMR 0x0000001c
#define CLASSD_ISR 0x00000020
#define CLASSD_WPMR 0x000000e4
#endif
sound/soc/codecs/da7213.c
View file @
7034ef5f
...
...
@@ -12,6 +12,7 @@
* option) any later version.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
...
...
@@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
struct
da7213_priv
*
da7213
=
snd_soc_codec_get_drvdata
(
codec
);
int
ret
=
0
;
if
((
da7213
->
clk_src
==
clk_id
)
&&
(
da7213
->
mclk_rate
==
freq
))
return
0
;
if
(((
freq
<
5000000
)
&&
(
freq
!=
32768
))
||
(
freq
>
54000000
))
{
dev_err
(
codec_dai
->
dev
,
"Unsupported MCLK value %d
\n
"
,
freq
);
return
-
EINVAL
;
}
switch
(
clk_id
)
{
case
DA7213_CLKSRC_MCLK
:
if
((
freq
==
32768
)
||
((
freq
>=
5000000
)
&&
(
freq
<=
54000000
)))
{
da7213
->
mclk_rate
=
freq
;
return
0
;
}
else
{
dev_err
(
codec_dai
->
dev
,
"Unsupported MCLK value %d
\n
"
,
freq
);
return
-
EINVAL
;
}
da7213
->
mclk_squarer_en
=
false
;
break
;
case
DA7213_CLKSRC_MCLK_SQR
:
da7213
->
mclk_squarer_en
=
true
;
break
;
default:
dev_err
(
codec_dai
->
dev
,
"Unknown clock source %d
\n
"
,
clk_id
);
return
-
EINVAL
;
}
da7213
->
clk_src
=
clk_id
;
if
(
da7213
->
mclk
)
{
freq
=
clk_round_rate
(
da7213
->
mclk
,
freq
);
ret
=
clk_set_rate
(
da7213
->
mclk
,
freq
);
if
(
ret
)
{
dev_err
(
codec_dai
->
dev
,
"Failed to set clock rate %d
\n
"
,
freq
);
return
ret
;
}
}
da7213
->
mclk_rate
=
freq
;
return
0
;
}
/* Supported PLL input frequencies are 5MHz - 54MHz. */
...
...
@@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = {
static
int
da7213_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
struct
da7213_priv
*
da7213
=
snd_soc_codec_get_drvdata
(
codec
);
int
ret
;
switch
(
level
)
{
case
SND_SOC_BIAS_ON
:
case
SND_SOC_BIAS_PREPARE
:
break
;
case
SND_SOC_BIAS_STANDBY
:
if
(
snd_soc_codec_get_bias_level
(
codec
)
==
SND_SOC_BIAS_OFF
)
{
/* MCLK */
if
(
da7213
->
mclk
)
{
ret
=
clk_prepare_enable
(
da7213
->
mclk
);
if
(
ret
)
{
dev_err
(
codec
->
dev
,
"Failed to enable mclk
\n
"
);
return
ret
;
}
}
/* Enable VMID reference & master bias */
snd_soc_update_bits
(
codec
,
DA7213_REFERENCES
,
DA7213_VMID_EN
|
DA7213_BIAS_EN
,
...
...
@@ -1382,15 +1417,127 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
/* Disable VMID reference & master bias */
snd_soc_update_bits
(
codec
,
DA7213_REFERENCES
,
DA7213_VMID_EN
|
DA7213_BIAS_EN
,
0
);
/* MCLK */
if
(
da7213
->
mclk
)
clk_disable_unprepare
(
da7213
->
mclk
);
break
;
}
return
0
;
}
/* DT */
static
const
struct
of_device_id
da7213_of_match
[]
=
{
{
.
compatible
=
"dlg,da7213"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
da7213_of_match
);
static
enum
da7213_micbias_voltage
da7213_of_micbias_lvl
(
struct
snd_soc_codec
*
codec
,
u32
val
)
{
switch
(
val
)
{
case
1600
:
return
DA7213_MICBIAS_1_6V
;
case
2200
:
return
DA7213_MICBIAS_2_2V
;
case
2500
:
return
DA7213_MICBIAS_2_5V
;
case
3000
:
return
DA7213_MICBIAS_3_0V
;
default:
dev_warn
(
codec
->
dev
,
"Invalid micbias level
\n
"
);
return
DA7213_MICBIAS_2_2V
;
}
}
static
enum
da7213_dmic_data_sel
da7213_of_dmic_data_sel
(
struct
snd_soc_codec
*
codec
,
const
char
*
str
)
{
if
(
!
strcmp
(
str
,
"lrise_rfall"
))
{
return
DA7213_DMIC_DATA_LRISE_RFALL
;
}
else
if
(
!
strcmp
(
str
,
"lfall_rrise"
))
{
return
DA7213_DMIC_DATA_LFALL_RRISE
;
}
else
{
dev_warn
(
codec
->
dev
,
"Invalid DMIC data select type
\n
"
);
return
DA7213_DMIC_DATA_LRISE_RFALL
;
}
}
static
enum
da7213_dmic_samplephase
da7213_of_dmic_samplephase
(
struct
snd_soc_codec
*
codec
,
const
char
*
str
)
{
if
(
!
strcmp
(
str
,
"on_clkedge"
))
{
return
DA7213_DMIC_SAMPLE_ON_CLKEDGE
;
}
else
if
(
!
strcmp
(
str
,
"between_clkedge"
))
{
return
DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE
;
}
else
{
dev_warn
(
codec
->
dev
,
"Invalid DMIC sample phase
\n
"
);
return
DA7213_DMIC_SAMPLE_ON_CLKEDGE
;
}
}
static
enum
da7213_dmic_clk_rate
da7213_of_dmic_clkrate
(
struct
snd_soc_codec
*
codec
,
u32
val
)
{
switch
(
val
)
{
case
1500000
:
return
DA7213_DMIC_CLK_1_5MHZ
;
case
3000000
:
return
DA7213_DMIC_CLK_3_0MHZ
;
default:
dev_warn
(
codec
->
dev
,
"Invalid DMIC clock rate
\n
"
);
return
DA7213_DMIC_CLK_1_5MHZ
;
}
}
static
struct
da7213_platform_data
*
da7213_of_to_pdata
(
struct
snd_soc_codec
*
codec
)
{
struct
device_node
*
np
=
codec
->
dev
->
of_node
;
struct
da7213_platform_data
*
pdata
;
const
char
*
of_str
;
u32
of_val32
;
pdata
=
devm_kzalloc
(
codec
->
dev
,
sizeof
(
*
pdata
),
GFP_KERNEL
);
if
(
!
pdata
)
{
dev_warn
(
codec
->
dev
,
"Failed to allocate memory for pdata
\n
"
);
return
NULL
;
}
if
(
of_property_read_u32
(
np
,
"dlg,micbias1-lvl"
,
&
of_val32
)
>=
0
)
pdata
->
micbias1_lvl
=
da7213_of_micbias_lvl
(
codec
,
of_val32
);
else
pdata
->
micbias1_lvl
=
DA7213_MICBIAS_2_2V
;
if
(
of_property_read_u32
(
np
,
"dlg,micbias2-lvl"
,
&
of_val32
)
>=
0
)
pdata
->
micbias2_lvl
=
da7213_of_micbias_lvl
(
codec
,
of_val32
);
else
pdata
->
micbias2_lvl
=
DA7213_MICBIAS_2_2V
;
if
(
!
of_property_read_string
(
np
,
"dlg,dmic-data-sel"
,
&
of_str
))
pdata
->
dmic_data_sel
=
da7213_of_dmic_data_sel
(
codec
,
of_str
);
else
pdata
->
dmic_data_sel
=
DA7213_DMIC_DATA_LRISE_RFALL
;
if
(
!
of_property_read_string
(
np
,
"dlg,dmic-samplephase"
,
&
of_str
))
pdata
->
dmic_samplephase
=
da7213_of_dmic_samplephase
(
codec
,
of_str
);
else
pdata
->
dmic_samplephase
=
DA7213_DMIC_SAMPLE_ON_CLKEDGE
;
if
(
of_property_read_u32
(
np
,
"dlg,dmic-clkrate"
,
&
of_val32
)
>=
0
)
pdata
->
dmic_clk_rate
=
da7213_of_dmic_clkrate
(
codec
,
of_val32
);
else
pdata
->
dmic_clk_rate
=
DA7213_DMIC_CLK_3_0MHZ
;
return
pdata
;
}
static
int
da7213_probe
(
struct
snd_soc_codec
*
codec
)
{
struct
da7213_priv
*
da7213
=
snd_soc_codec_get_drvdata
(
codec
);
struct
da7213_platform_data
*
pdata
=
da7213
->
pdata
;
/* Default to using ALC auto offset calibration mode. */
snd_soc_update_bits
(
codec
,
DA7213_ALC_CTRL1
,
...
...
@@ -1450,8 +1597,15 @@ static int da7213_probe(struct snd_soc_codec *codec)
snd_soc_update_bits
(
codec
,
DA7213_LINE_CTRL
,
DA7213_LINE_AMP_OE
,
DA7213_LINE_AMP_OE
);
/* Handle DT/Platform data */
if
(
codec
->
dev
->
of_node
)
da7213
->
pdata
=
da7213_of_to_pdata
(
codec
);
else
da7213
->
pdata
=
dev_get_platdata
(
codec
->
dev
);
/* Set platform data values */
if
(
da7213
->
pdata
)
{
struct
da7213_platform_data
*
pdata
=
da7213
->
pdata
;
u8
micbias_lvl
=
0
,
dmic_cfg
=
0
;
/* Set Mic Bias voltages */
...
...
@@ -1503,10 +1657,17 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_DATA_SEL_MASK
|
DA7213_DMIC_SAMPLEPHASE_MASK
|
DA7213_DMIC_CLK_RATE_MASK
,
dmic_cfg
);
}
/* Set MCLK squaring */
da7213
->
mclk_squarer_en
=
pdata
->
mclk_squaring
;
/* Check if MCLK provided */
da7213
->
mclk
=
devm_clk_get
(
codec
->
dev
,
"mclk"
);
if
(
IS_ERR
(
da7213
->
mclk
))
{
if
(
PTR_ERR
(
da7213
->
mclk
)
!=
-
ENOENT
)
return
PTR_ERR
(
da7213
->
mclk
);
else
da7213
->
mclk
=
NULL
;
}
return
0
;
}
...
...
@@ -1537,7 +1698,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
const
struct
i2c_device_id
*
id
)
{
struct
da7213_priv
*
da7213
;
struct
da7213_platform_data
*
pdata
=
dev_get_platdata
(
&
i2c
->
dev
);
int
ret
;
da7213
=
devm_kzalloc
(
&
i2c
->
dev
,
sizeof
(
struct
da7213_priv
),
...
...
@@ -1545,9 +1705,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
if
(
!
da7213
)
return
-
ENOMEM
;
if
(
pdata
)
da7213
->
pdata
=
pdata
;
i2c_set_clientdata
(
i2c
,
da7213
);
da7213
->
regmap
=
devm_regmap_init_i2c
(
i2c
,
&
da7213_regmap_config
);
...
...
@@ -1582,6 +1739,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
static
struct
i2c_driver
da7213_i2c_driver
=
{
.
driver
=
{
.
name
=
"da7213"
,
.
of_match_table
=
of_match_ptr
(
da7213_of_match
),
},
.
probe
=
da7213_i2c_probe
,
.
remove
=
da7213_remove
,
...
...
sound/soc/codecs/da7213.h
View file @
7034ef5f
...
...
@@ -13,6 +13,7 @@
#ifndef _DA7213_H
#define _DA7213_H
#include <linux/clk.h>
#include <linux/regmap.h>
#include <sound/da7213.h>
...
...
@@ -504,14 +505,17 @@
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
enum
clk_src
{
DA7213_CLKSRC_MCLK
enum
da7213_clk_src
{
DA7213_CLKSRC_MCLK
=
0
,
DA7213_CLKSRC_MCLK_SQR
,
};
/* Codec private data */
struct
da7213_priv
{
struct
regmap
*
regmap
;
struct
clk
*
mclk
;
unsigned
int
mclk_rate
;
int
clk_src
;
bool
master
;
bool
mclk_squarer_en
;
bool
srm_en
;
...
...
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