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
3db5de56
Commit
3db5de56
authored
Oct 26, 2015
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branches 'asoc/topic/nau8825' and 'asoc/topic/pxa' into asoc-next
parents
8707344e
218d2ce2
58ceb57e
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1843 additions
and
228 deletions
+1843
-228
Documentation/devicetree/bindings/sound/nau8825.txt
Documentation/devicetree/bindings/sound/nau8825.txt
+102
-0
include/sound/pxa2xx-lib.h
include/sound/pxa2xx-lib.h
+0
-1
sound/arm/pxa2xx-ac97.c
sound/arm/pxa2xx-ac97.c
+11
-2
sound/arm/pxa2xx-pcm-lib.c
sound/arm/pxa2xx-pcm-lib.c
+36
-165
sound/arm/pxa2xx-pcm.c
sound/arm/pxa2xx-pcm.c
+3
-9
sound/arm/pxa2xx-pcm.h
sound/arm/pxa2xx-pcm.h
+0
-2
sound/soc/codecs/Kconfig
sound/soc/codecs/Kconfig
+4
-0
sound/soc/codecs/Makefile
sound/soc/codecs/Makefile
+2
-0
sound/soc/codecs/nau8825.c
sound/soc/codecs/nau8825.c
+1309
-0
sound/soc/codecs/nau8825.h
sound/soc/codecs/nau8825.h
+341
-0
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/pxa2xx-ac97.c
+32
-17
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/pxa/pxa2xx-i2s.c
+3
-0
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/pxa2xx-pcm.c
+0
-32
No files found.
Documentation/devicetree/bindings/sound/nau8825.txt
0 → 100644
View file @
3db5de56
Nuvoton NAU8825 audio codec
This device supports I2C only.
Required properties:
- compatible : Must be "nuvoton,nau8825"
- reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1).
Optional properties:
- nuvoton,jkdet-enable: Enable jack detection via JKDET pin.
- nuvoton,jkdet-pull-enable: Enable JKDET pin pull. If set - pin pull enabled,
otherwise pin in high impedance state.
- nuvoton,jkdet-pull-up: Pull-up JKDET pin. If set then JKDET pin is pull up, otherwise pull down.
- nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low.
- nuvoton,vref-impedance: VREF Impedance selection
0 - Open
1 - 25 kOhm
2 - 125 kOhm
3 - 2.5 kOhm
- nuvoton,micbias-voltage: Micbias voltage level.
0 - VDDA
1 - VDDA
2 - VDDA * 1.1
3 - VDDA * 1.2
4 - VDDA * 1.3
5 - VDDA * 1.4
6 - VDDA * 1.53
7 - VDDA * 1.53
- nuvoton,sar-threshold-num: Number of buttons supported
- nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as
SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R)
where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance.
Refer datasheet section 10.2 for more information about threshold calculation.
- nuvoton,sar-hysteresis: Button impedance measurement hysteresis.
- nuvoton,sar-voltage: Reference voltage for button impedance measurement.
0 - VDDA
1 - VDDA
2 - VDDA * 1.1
3 - VDDA * 1.2
4 - VDDA * 1.3
5 - VDDA * 1.4
6 - VDDA * 1.53
7 - VDDA * 1.53
- nuvoton,sar-compare-time: SAR compare time
0 - 500 ns
1 - 1 us
2 - 2 us
3 - 4 us
- nuvoton,sar-sampling-time: SAR sampling time
0 - 2 us
1 - 4 us
2 - 8 us
3 - 16 us
- nuvoton,short-key-debounce: Button short key press debounce time.
0 - 30 ms
1 - 50 ms
2 - 100 ms
3 - 30 ms
- nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
- nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
- clocks: list of phandle and clock specifier pairs according to common clock bindings for the
clocks described in clock-names
- clock-names: should include "mclk" for the MCLK master clock
Example:
headset: nau8825@1a {
compatible = "nuvoton,nau8825";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(E, 6) IRQ_TYPE_LEVEL_LOW>;
nuvoton,jkdet-enable;
nuvoton,jkdet-pull-enable;
nuvoton,jkdet-pull-up;
nuvoton,jkdet-polarity = <GPIO_ACTIVE_LOW>;
nuvoton,vref-impedance = <2>;
nuvoton,micbias-voltage = <6>;
// Setup 4 buttons impedance according to Android specification
nuvoton,sar-threshold-num = <4>;
nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>;
nuvoton,sar-hysteresis = <1>;
nuvoton,sar-voltage = <0>;
nuvoton,sar-compare-time = <0>;
nuvoton,sar-sampling-time = <0>;
nuvoton,short-key-debounce = <2>;
nuvoton,jack-insert-debounce = <7>;
nuvoton,jack-eject-debounce = <7>;
clock-names = "mclk";
clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>;
};
include/sound/pxa2xx-lib.h
View file @
3db5de56
...
...
@@ -12,7 +12,6 @@ extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream);
extern
int
pxa2xx_pcm_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
);
extern
snd_pcm_uframes_t
pxa2xx_pcm_pointer
(
struct
snd_pcm_substream
*
substream
);
extern
int
__pxa2xx_pcm_prepare
(
struct
snd_pcm_substream
*
substream
);
extern
void
pxa2xx_pcm_dma_irq
(
int
dma_ch
,
void
*
dev_id
);
extern
int
__pxa2xx_pcm_open
(
struct
snd_pcm_substream
*
substream
);
extern
int
__pxa2xx_pcm_close
(
struct
snd_pcm_substream
*
substream
);
extern
int
pxa2xx_pcm_mmap
(
struct
snd_pcm_substream
*
substream
,
...
...
sound/arm/pxa2xx-ac97.c
View file @
3db5de56
...
...
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
...
...
@@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.
reset
=
pxa2xx_ac97_reset
,
};
static
unsigned
long
pxa2xx_ac97_pcm_out_req
=
12
;
static
struct
pxad_param
pxa2xx_ac97_pcm_out_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
12
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_out
=
{
.
addr
=
__PREG
(
PCDR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_4_BYTES
,
...
...
@@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
.
filter_data
=
&
pxa2xx_ac97_pcm_out_req
,
};
static
unsigned
long
pxa2xx_ac97_pcm_in_req
=
11
;
static
struct
pxad_param
pxa2xx_ac97_pcm_in_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
11
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_in
=
{
.
addr
=
__PREG
(
PCDR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_4_BYTES
,
...
...
sound/arm/pxa2xx-pcm-lib.c
View file @
3db5de56
...
...
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
...
...
@@ -15,8 +16,6 @@
#include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h>
#include <mach/dma.h>
#include "pxa2xx-pcm.h"
static
const
struct
snd_pcm_hardware
pxa2xx_pcm_hardware
=
{
...
...
@@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8192
-
32
,
.
periods_min
=
1
,
.
periods_max
=
PAGE_SIZE
/
sizeof
(
pxa_dma_desc
)
,
.
periods_max
=
256
,
.
buffer_bytes_max
=
128
*
1024
,
.
fifo_size
=
32
,
};
...
...
@@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
int
__pxa2xx_pcm_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
pxa2xx_runtime_data
*
rtd
=
runtime
->
private_data
;
size_t
totsize
=
params_buffer_bytes
(
params
);
size_t
period
=
params_period_bytes
(
params
);
pxa_dma_desc
*
dma_desc
;
dma_addr_t
dma_buff_phys
,
next_desc_phys
;
u32
dcmd
=
DCMD_INCSRCADDR
|
DCMD_FLOWTRG
;
struct
dma_chan
*
chan
=
snd_dmaengine_pcm_get_chan
(
substream
);
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
snd_dmaengine_dai_dma_data
*
dma_params
;
struct
dma_slave_config
config
;
int
ret
;
/* temporary transition hack */
switch
(
rtd
->
params
->
addr_width
)
{
case
DMA_SLAVE_BUSWIDTH_1_BYTE
:
dcmd
|=
DCMD_WIDTH1
;
break
;
case
DMA_SLAVE_BUSWIDTH_2_BYTES
:
dcmd
|=
DCMD_WIDTH2
;
break
;
case
DMA_SLAVE_BUSWIDTH_4_BYTES
:
dcmd
|=
DCMD_WIDTH4
;
break
;
default:
/* can't happen */
break
;
}
dma_params
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
if
(
!
dma_params
)
return
0
;
switch
(
rtd
->
params
->
maxburst
)
{
case
8
:
dcmd
|=
DCMD_BURST8
;
break
;
case
16
:
dcmd
|=
DCMD_BURST16
;
break
;
case
32
:
dcmd
|=
DCMD_BURST32
;
break
;
}
ret
=
snd_hwparams_to_dma_slave_config
(
substream
,
params
,
&
config
);
if
(
ret
)
return
ret
;
snd_pcm_set_runtime_buffer
(
substream
,
&
substream
->
dma_buffer
);
runtime
->
dma_bytes
=
totsize
;
snd_dmaengine_pcm_set_config_from_dai_data
(
substream
,
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
),
&
config
);
dma_desc
=
rtd
->
dma_desc_array
;
next_desc_phys
=
rtd
->
dma_desc_array_phys
;
dma_buff_phys
=
runtime
->
dma_addr
;
do
{
next_desc_phys
+=
sizeof
(
pxa_dma_desc
);
dma_desc
->
ddadr
=
next_desc_phys
;
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
{
dma_desc
->
dsadr
=
dma_buff_phys
;
dma_desc
->
dtadr
=
rtd
->
params
->
addr
;
}
else
{
dma_desc
->
dsadr
=
rtd
->
params
->
addr
;
dma_desc
->
dtadr
=
dma_buff_phys
;
}
if
(
period
>
totsize
)
period
=
totsize
;
dma_desc
->
dcmd
=
dcmd
|
period
|
DCMD_ENDIRQEN
;
dma_desc
++
;
dma_buff_phys
+=
period
;
}
while
(
totsize
-=
period
);
dma_desc
[
-
1
].
ddadr
=
rtd
->
dma_desc_array_phys
;
ret
=
dmaengine_slave_config
(
chan
,
&
config
);
if
(
ret
)
return
ret
;
snd_pcm_set_runtime_buffer
(
substream
,
&
substream
->
dma_buffer
);
return
0
;
}
...
...
@@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
int
__pxa2xx_pcm_hw_free
(
struct
snd_pcm_substream
*
substream
)
{
struct
pxa2xx_runtime_data
*
rtd
=
substream
->
runtime
->
private_data
;
if
(
rtd
&&
rtd
->
params
&&
rtd
->
params
->
filter_data
)
{
unsigned
long
req
=
*
(
unsigned
long
*
)
rtd
->
params
->
filter_data
;
DRCMR
(
req
)
=
0
;
}
snd_pcm_set_runtime_buffer
(
substream
,
NULL
);
return
0
;
}
...
...
@@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
int
pxa2xx_pcm_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
pxa2xx_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
int
ret
=
0
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
DDADR
(
prtd
->
dma_ch
)
=
prtd
->
dma_desc_array_phys
;
DCSR
(
prtd
->
dma_ch
)
=
DCSR_RUN
;
break
;
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
DCSR
(
prtd
->
dma_ch
)
&=
~
DCSR_RUN
;
break
;
case
SNDRV_PCM_TRIGGER_RESUME
:
DCSR
(
prtd
->
dma_ch
)
|=
DCSR_RUN
;
break
;
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
DDADR
(
prtd
->
dma_ch
)
=
prtd
->
dma_desc_array_phys
;
DCSR
(
prtd
->
dma_ch
)
|=
DCSR_RUN
;
break
;
default:
ret
=
-
EINVAL
;
}
return
ret
;
return
snd_dmaengine_pcm_trigger
(
substream
,
cmd
);
}
EXPORT_SYMBOL
(
pxa2xx_pcm_trigger
);
snd_pcm_uframes_t
pxa2xx_pcm_pointer
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
pxa2xx_runtime_data
*
prtd
=
runtime
->
private_data
;
dma_addr_t
ptr
=
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
?
DSADR
(
prtd
->
dma_ch
)
:
DTADR
(
prtd
->
dma_ch
);
snd_pcm_uframes_t
x
=
bytes_to_frames
(
runtime
,
ptr
-
runtime
->
dma_addr
);
if
(
x
==
runtime
->
buffer_size
)
x
=
0
;
return
x
;
return
snd_dmaengine_pcm_pointer
(
substream
);
}
EXPORT_SYMBOL
(
pxa2xx_pcm_pointer
);
int
__pxa2xx_pcm_prepare
(
struct
snd_pcm_substream
*
substream
)
{
struct
pxa2xx_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
unsigned
long
req
;
if
(
!
prtd
||
!
prtd
->
params
)
return
0
;
if
(
prtd
->
dma_ch
==
-
1
)
return
-
EINVAL
;
DCSR
(
prtd
->
dma_ch
)
&=
~
DCSR_RUN
;
DCSR
(
prtd
->
dma_ch
)
=
0
;
DCMD
(
prtd
->
dma_ch
)
=
0
;
req
=
*
(
unsigned
long
*
)
prtd
->
params
->
filter_data
;
DRCMR
(
req
)
=
prtd
->
dma_ch
|
DRCMR_MAPVLD
;
return
0
;
}
EXPORT_SYMBOL
(
__pxa2xx_pcm_prepare
);
void
pxa2xx_pcm_dma_irq
(
int
dma_ch
,
void
*
dev_id
)
{
struct
snd_pcm_substream
*
substream
=
dev_id
;
int
dcsr
;
dcsr
=
DCSR
(
dma_ch
);
DCSR
(
dma_ch
)
=
dcsr
&
~
DCSR_STOPIRQEN
;
if
(
dcsr
&
DCSR_ENDINTR
)
{
snd_pcm_period_elapsed
(
substream
);
}
else
{
printk
(
KERN_ERR
"DMA error on channel %d (DCSR=%#x)
\n
"
,
dma_ch
,
dcsr
);
snd_pcm_stop_xrun
(
substream
);
}
}
EXPORT_SYMBOL
(
pxa2xx_pcm_dma_irq
);
int
__pxa2xx_pcm_open
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
pxa2xx_runtime_data
*
rtd
;
struct
snd_dmaengine_dai_dma_data
*
dma_params
;
int
ret
;
runtime
->
hw
=
pxa2xx_pcm_hardware
;
dma_params
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
if
(
!
dma_params
)
return
0
;
/*
* For mysterious reasons (and despite what the manual says)
* playback samples are lost if the DMA count is not a multiple
...
...
@@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
ret
=
snd_pcm_hw_constraint_step
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES
,
32
);
if
(
ret
)
goto
ou
t
;
return
re
t
;
ret
=
snd_pcm_hw_constraint_step
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
32
);
if
(
ret
)
goto
ou
t
;
return
re
t
;
ret
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
);
if
(
ret
<
0
)
goto
out
;
ret
=
-
ENOMEM
;
rtd
=
kzalloc
(
sizeof
(
*
rtd
),
GFP_KERNEL
);
if
(
!
rtd
)
goto
out
;
rtd
->
dma_desc_array
=
dma_alloc_writecombine
(
substream
->
pcm
->
card
->
dev
,
PAGE_SIZE
,
&
rtd
->
dma_desc_array_phys
,
GFP_KERNEL
);
if
(
!
rtd
->
dma_desc_array
)
goto
err1
;
rtd
->
dma_ch
=
-
1
;
runtime
->
private_data
=
rtd
;
return
0
;
err1:
kfree
(
rtd
);
out:
return
ret
;
return
snd_dmaengine_pcm_open_request_chan
(
substream
,
pxad_filter_fn
,
dma_params
->
filter_data
);
}
EXPORT_SYMBOL
(
__pxa2xx_pcm_open
);
int
__pxa2xx_pcm_close
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
pxa2xx_runtime_data
*
rtd
=
runtime
->
private_data
;
dma_free_writecombine
(
substream
->
pcm
->
card
->
dev
,
PAGE_SIZE
,
rtd
->
dma_desc_array
,
rtd
->
dma_desc_array_phys
);
kfree
(
rtd
);
return
0
;
return
snd_dmaengine_pcm_close_release_chan
(
substream
);
}
EXPORT_SYMBOL
(
__pxa2xx_pcm_close
);
...
...
sound/arm/pxa2xx-pcm.c
View file @
3db5de56
...
...
@@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
rtd
->
params
=
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
?
client
->
playback_params
:
client
->
capture_params
;
ret
=
pxa_request_dma
(
"dma"
,
DMA_PRIO_LOW
,
pxa2xx_pcm_dma_irq
,
substream
);
if
(
ret
<
0
)
goto
err2
;
rtd
->
dma_ch
=
ret
;
ret
=
client
->
startup
(
substream
);
if
(
!
ret
)
goto
out
;
goto
err2
;
return
0
;
pxa_free_dma
(
rtd
->
dma_ch
);
err2:
__pxa2xx_pcm_close
(
substream
);
out:
...
...
@@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
static
int
pxa2xx_pcm_close
(
struct
snd_pcm_substream
*
substream
)
{
struct
pxa2xx_pcm_client
*
client
=
substream
->
private_data
;
struct
pxa2xx_runtime_data
*
rtd
=
substream
->
runtime
->
private_data
;
pxa_free_dma
(
rtd
->
dma_ch
);
client
->
shutdown
(
substream
);
return
__pxa2xx_pcm_close
(
substream
);
...
...
sound/arm/pxa2xx-pcm.h
View file @
3db5de56
...
...
@@ -13,8 +13,6 @@
struct
pxa2xx_runtime_data
{
int
dma_ch
;
struct
snd_dmaengine_dai_dma_data
*
params
;
struct
pxa_dma_desc
*
dma_desc_array
;
dma_addr_t
dma_desc_array_phys
;
};
struct
pxa2xx_pcm_client
{
...
...
sound/soc/codecs/Kconfig
View file @
3db5de56
...
...
@@ -81,6 +81,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
...
...
@@ -907,6 +908,9 @@ config SND_SOC_MC13783
config SND_SOC_ML26124
tristate
config SND_SOC_NAU8825
tristate
config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
...
...
sound/soc/codecs/Makefile
View file @
3db5de56
...
...
@@ -74,6 +74,7 @@ snd-soc-max98925-objs := max98925.o
snd-soc-max9850-objs
:=
max9850.o
snd-soc-mc13783-objs
:=
mc13783.o
snd-soc-ml26124-objs
:=
ml26124.o
snd-soc-nau8825-objs
:=
nau8825.o
snd-soc-pcm1681-objs
:=
pcm1681.o
snd-soc-pcm1792a-codec-objs
:=
pcm1792a.o
snd-soc-pcm3008-objs
:=
pcm3008.o
...
...
@@ -268,6 +269,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX9850)
+=
snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783)
+=
snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124)
+=
snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_NAU8825)
+=
snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681)
+=
snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A)
+=
snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008)
+=
snd-soc-pcm3008.o
...
...
sound/soc/codecs/nau8825.c
0 → 100644
View file @
3db5de56
/*
* Nuvoton NAU8825 audio codec driver
*
* Copyright 2015 Google Chromium project.
* Author: Anatol Pomozov <anatol@chromium.org>
* Copyright 2015 Nuvoton Technology Corp.
* Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/acpi.h>
#include <linux/math64.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "nau8825.h"
#define NAU_FREF_MAX 13500000
#define NAU_FVCO_MAX 100000000
#define NAU_FVCO_MIN 90000000
struct
nau8825_fll
{
int
mclk_src
;
int
ratio
;
int
fll_frac
;
int
fll_int
;
int
clk_ref_div
;
};
struct
nau8825_fll_attr
{
unsigned
int
param
;
unsigned
int
val
;
};
/* scaling for mclk from sysclk_src output */
static
const
struct
nau8825_fll_attr
mclk_src_scaling
[]
=
{
{
1
,
0x0
},
{
2
,
0x2
},
{
4
,
0x3
},
{
8
,
0x4
},
{
16
,
0x5
},
{
32
,
0x6
},
{
3
,
0x7
},
{
6
,
0xa
},
{
12
,
0xb
},
{
24
,
0xc
},
{
48
,
0xd
},
{
96
,
0xe
},
{
5
,
0xf
},
};
/* ratio for input clk freq */
static
const
struct
nau8825_fll_attr
fll_ratio
[]
=
{
{
512000
,
0x01
},
{
256000
,
0x02
},
{
128000
,
0x04
},
{
64000
,
0x08
},
{
32000
,
0x10
},
{
8000
,
0x20
},
{
4000
,
0x40
},
};
static
const
struct
nau8825_fll_attr
fll_pre_scalar
[]
=
{
{
1
,
0x0
},
{
2
,
0x1
},
{
4
,
0x2
},
{
8
,
0x3
},
};
static
const
struct
reg_default
nau8825_reg_defaults
[]
=
{
{
NAU8825_REG_ENA_CTRL
,
0x00ff
},
{
NAU8825_REG_CLK_DIVIDER
,
0x0050
},
{
NAU8825_REG_FLL1
,
0x0
},
{
NAU8825_REG_FLL2
,
0x3126
},
{
NAU8825_REG_FLL3
,
0x0008
},
{
NAU8825_REG_FLL4
,
0x0010
},
{
NAU8825_REG_FLL5
,
0x0
},
{
NAU8825_REG_FLL6
,
0x6000
},
{
NAU8825_REG_FLL_VCO_RSV
,
0xf13c
},
{
NAU8825_REG_HSD_CTRL
,
0x000c
},
{
NAU8825_REG_JACK_DET_CTRL
,
0x0
},
{
NAU8825_REG_INTERRUPT_MASK
,
0x0
},
{
NAU8825_REG_INTERRUPT_DIS_CTRL
,
0xffff
},
{
NAU8825_REG_SAR_CTRL
,
0x0015
},
{
NAU8825_REG_KEYDET_CTRL
,
0x0110
},
{
NAU8825_REG_VDET_THRESHOLD_1
,
0x0
},
{
NAU8825_REG_VDET_THRESHOLD_2
,
0x0
},
{
NAU8825_REG_VDET_THRESHOLD_3
,
0x0
},
{
NAU8825_REG_VDET_THRESHOLD_4
,
0x0
},
{
NAU8825_REG_GPIO34_CTRL
,
0x0
},
{
NAU8825_REG_GPIO12_CTRL
,
0x0
},
{
NAU8825_REG_TDM_CTRL
,
0x0
},
{
NAU8825_REG_I2S_PCM_CTRL1
,
0x000b
},
{
NAU8825_REG_I2S_PCM_CTRL2
,
0x8010
},
{
NAU8825_REG_LEFT_TIME_SLOT
,
0x0
},
{
NAU8825_REG_RIGHT_TIME_SLOT
,
0x0
},
{
NAU8825_REG_BIQ_CTRL
,
0x0
},
{
NAU8825_REG_BIQ_COF1
,
0x0
},
{
NAU8825_REG_BIQ_COF2
,
0x0
},
{
NAU8825_REG_BIQ_COF3
,
0x0
},
{
NAU8825_REG_BIQ_COF4
,
0x0
},
{
NAU8825_REG_BIQ_COF5
,
0x0
},
{
NAU8825_REG_BIQ_COF6
,
0x0
},
{
NAU8825_REG_BIQ_COF7
,
0x0
},
{
NAU8825_REG_BIQ_COF8
,
0x0
},
{
NAU8825_REG_BIQ_COF9
,
0x0
},
{
NAU8825_REG_BIQ_COF10
,
0x0
},
{
NAU8825_REG_ADC_RATE
,
0x0010
},
{
NAU8825_REG_DAC_CTRL1
,
0x0001
},
{
NAU8825_REG_DAC_CTRL2
,
0x0
},
{
NAU8825_REG_DAC_DGAIN_CTRL
,
0x0
},
{
NAU8825_REG_ADC_DGAIN_CTRL
,
0x00cf
},
{
NAU8825_REG_MUTE_CTRL
,
0x0
},
{
NAU8825_REG_HSVOL_CTRL
,
0x0
},
{
NAU8825_REG_DACL_CTRL
,
0x02cf
},
{
NAU8825_REG_DACR_CTRL
,
0x00cf
},
{
NAU8825_REG_ADC_DRC_KNEE_IP12
,
0x1486
},
{
NAU8825_REG_ADC_DRC_KNEE_IP34
,
0x0f12
},
{
NAU8825_REG_ADC_DRC_SLOPES
,
0x25ff
},
{
NAU8825_REG_ADC_DRC_ATKDCY
,
0x3457
},
{
NAU8825_REG_DAC_DRC_KNEE_IP12
,
0x1486
},
{
NAU8825_REG_DAC_DRC_KNEE_IP34
,
0x0f12
},
{
NAU8825_REG_DAC_DRC_SLOPES
,
0x25f9
},
{
NAU8825_REG_DAC_DRC_ATKDCY
,
0x3457
},
{
NAU8825_REG_IMM_MODE_CTRL
,
0x0
},
{
NAU8825_REG_CLASSG_CTRL
,
0x0
},
{
NAU8825_REG_OPT_EFUSE_CTRL
,
0x0
},
{
NAU8825_REG_MISC_CTRL
,
0x0
},
{
NAU8825_REG_BIAS_ADJ
,
0x0
},
{
NAU8825_REG_TRIM_SETTINGS
,
0x0
},
{
NAU8825_REG_ANALOG_CONTROL_1
,
0x0
},
{
NAU8825_REG_ANALOG_CONTROL_2
,
0x0
},
{
NAU8825_REG_ANALOG_ADC_1
,
0x0011
},
{
NAU8825_REG_ANALOG_ADC_2
,
0x0020
},
{
NAU8825_REG_RDAC
,
0x0008
},
{
NAU8825_REG_MIC_BIAS
,
0x0006
},
{
NAU8825_REG_BOOST
,
0x0
},
{
NAU8825_REG_FEPGA
,
0x0
},
{
NAU8825_REG_POWER_UP_CONTROL
,
0x0
},
{
NAU8825_REG_CHARGE_PUMP
,
0x0
},
};
static
bool
nau8825_readable_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
NAU8825_REG_ENA_CTRL
:
case
NAU8825_REG_CLK_DIVIDER
...
NAU8825_REG_FLL_VCO_RSV
:
case
NAU8825_REG_HSD_CTRL
...
NAU8825_REG_JACK_DET_CTRL
:
case
NAU8825_REG_INTERRUPT_MASK
...
NAU8825_REG_KEYDET_CTRL
:
case
NAU8825_REG_VDET_THRESHOLD_1
...
NAU8825_REG_DACR_CTRL
:
case
NAU8825_REG_ADC_DRC_KNEE_IP12
...
NAU8825_REG_ADC_DRC_ATKDCY
:
case
NAU8825_REG_DAC_DRC_KNEE_IP12
...
NAU8825_REG_DAC_DRC_ATKDCY
:
case
NAU8825_REG_IMM_MODE_CTRL
...
NAU8825_REG_IMM_RMS_R
:
case
NAU8825_REG_CLASSG_CTRL
...
NAU8825_REG_OPT_EFUSE_CTRL
:
case
NAU8825_REG_MISC_CTRL
:
case
NAU8825_REG_I2C_DEVICE_ID
...
NAU8825_REG_SARDOUT_RAM_STATUS
:
case
NAU8825_REG_BIAS_ADJ
:
case
NAU8825_REG_TRIM_SETTINGS
...
NAU8825_REG_ANALOG_CONTROL_2
:
case
NAU8825_REG_ANALOG_ADC_1
...
NAU8825_REG_MIC_BIAS
:
case
NAU8825_REG_BOOST
...
NAU8825_REG_FEPGA
:
case
NAU8825_REG_POWER_UP_CONTROL
...
NAU8825_REG_GENERAL_STATUS
:
return
true
;
default:
return
false
;
}
}
static
bool
nau8825_writeable_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
NAU8825_REG_RESET
...
NAU8825_REG_ENA_CTRL
:
case
NAU8825_REG_CLK_DIVIDER
...
NAU8825_REG_FLL_VCO_RSV
:
case
NAU8825_REG_HSD_CTRL
...
NAU8825_REG_JACK_DET_CTRL
:
case
NAU8825_REG_INTERRUPT_MASK
:
case
NAU8825_REG_INT_CLR_KEY_STATUS
...
NAU8825_REG_KEYDET_CTRL
:
case
NAU8825_REG_VDET_THRESHOLD_1
...
NAU8825_REG_DACR_CTRL
:
case
NAU8825_REG_ADC_DRC_KNEE_IP12
...
NAU8825_REG_ADC_DRC_ATKDCY
:
case
NAU8825_REG_DAC_DRC_KNEE_IP12
...
NAU8825_REG_DAC_DRC_ATKDCY
:
case
NAU8825_REG_IMM_MODE_CTRL
:
case
NAU8825_REG_CLASSG_CTRL
...
NAU8825_REG_OPT_EFUSE_CTRL
:
case
NAU8825_REG_MISC_CTRL
:
case
NAU8825_REG_BIAS_ADJ
:
case
NAU8825_REG_TRIM_SETTINGS
...
NAU8825_REG_ANALOG_CONTROL_2
:
case
NAU8825_REG_ANALOG_ADC_1
...
NAU8825_REG_MIC_BIAS
:
case
NAU8825_REG_BOOST
...
NAU8825_REG_FEPGA
:
case
NAU8825_REG_POWER_UP_CONTROL
...
NAU8825_REG_CHARGE_PUMP
:
return
true
;
default:
return
false
;
}
}
static
bool
nau8825_volatile_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
NAU8825_REG_RESET
:
case
NAU8825_REG_IRQ_STATUS
:
case
NAU8825_REG_INT_CLR_KEY_STATUS
:
case
NAU8825_REG_IMM_RMS_L
:
case
NAU8825_REG_IMM_RMS_R
:
case
NAU8825_REG_I2C_DEVICE_ID
:
case
NAU8825_REG_SARDOUT_RAM_STATUS
:
case
NAU8825_REG_CHARGE_PUMP_INPUT_READ
:
case
NAU8825_REG_GENERAL_STATUS
:
return
true
;
default:
return
false
;
}
}
static
int
nau8825_pump_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
kcontrol
,
int
event
)
{
switch
(
event
)
{
case
SND_SOC_DAPM_POST_PMU
:
/* Prevent startup click by letting charge pump to ramp up */
msleep
(
10
);
break
;
default:
return
-
EINVAL
;
}
return
0
;
}
static
const
char
*
const
nau8825_adc_decimation
[]
=
{
"32"
,
"64"
,
"128"
,
"256"
};
static
const
struct
soc_enum
nau8825_adc_decimation_enum
=
SOC_ENUM_SINGLE
(
NAU8825_REG_ADC_RATE
,
NAU8825_ADC_SYNC_DOWN_SFT
,
ARRAY_SIZE
(
nau8825_adc_decimation
),
nau8825_adc_decimation
);
static
const
char
*
const
nau8825_dac_oversampl
[]
=
{
"64"
,
"256"
,
"128"
,
""
,
"32"
};
static
const
struct
soc_enum
nau8825_dac_oversampl_enum
=
SOC_ENUM_SINGLE
(
NAU8825_REG_DAC_CTRL1
,
NAU8825_DAC_OVERSAMPLE_SFT
,
ARRAY_SIZE
(
nau8825_dac_oversampl
),
nau8825_dac_oversampl
);
static
const
DECLARE_TLV_DB_MINMAX_MUTE
(
adc_vol_tlv
,
-
10300
,
2400
);
static
const
DECLARE_TLV_DB_MINMAX_MUTE
(
sidetone_vol_tlv
,
-
4200
,
0
);
static
const
DECLARE_TLV_DB_MINMAX
(
dac_vol_tlv
,
-
5400
,
0
);
static
const
DECLARE_TLV_DB_MINMAX
(
fepga_gain_tlv
,
-
100
,
3600
);
static
const
DECLARE_TLV_DB_MINMAX_MUTE
(
crosstalk_vol_tlv
,
-
9600
,
2400
);
static
const
struct
snd_kcontrol_new
nau8825_controls
[]
=
{
SOC_SINGLE_TLV
(
"Mic Volume"
,
NAU8825_REG_ADC_DGAIN_CTRL
,
0
,
0xff
,
0
,
adc_vol_tlv
),
SOC_DOUBLE_TLV
(
"Headphone Bypass Volume"
,
NAU8825_REG_ADC_DGAIN_CTRL
,
12
,
8
,
0x0f
,
0
,
sidetone_vol_tlv
),
SOC_DOUBLE_TLV
(
"Headphone Volume"
,
NAU8825_REG_HSVOL_CTRL
,
6
,
0
,
0x3f
,
1
,
dac_vol_tlv
),
SOC_SINGLE_TLV
(
"Frontend PGA Volume"
,
NAU8825_REG_POWER_UP_CONTROL
,
8
,
37
,
0
,
fepga_gain_tlv
),
SOC_DOUBLE_TLV
(
"Headphone Crosstalk Volume"
,
NAU8825_REG_DAC_DGAIN_CTRL
,
0
,
8
,
0xff
,
0
,
crosstalk_vol_tlv
),
SOC_ENUM
(
"ADC Decimation Rate"
,
nau8825_adc_decimation_enum
),
SOC_ENUM
(
"DAC Oversampling Rate"
,
nau8825_dac_oversampl_enum
),
};
/* DAC Mux 0x33[9] and 0x34[9] */
static
const
char
*
const
nau8825_dac_src
[]
=
{
"DACL"
,
"DACR"
,
};
static
SOC_ENUM_SINGLE_DECL
(
nau8825_dacl_enum
,
NAU8825_REG_DACL_CTRL
,
NAU8825_DACL_CH_SEL_SFT
,
nau8825_dac_src
);
static
SOC_ENUM_SINGLE_DECL
(
nau8825_dacr_enum
,
NAU8825_REG_DACR_CTRL
,
NAU8825_DACR_CH_SEL_SFT
,
nau8825_dac_src
);
static
const
struct
snd_kcontrol_new
nau8825_dacl_mux
=
SOC_DAPM_ENUM
(
"DACL Source"
,
nau8825_dacl_enum
);
static
const
struct
snd_kcontrol_new
nau8825_dacr_mux
=
SOC_DAPM_ENUM
(
"DACR Source"
,
nau8825_dacr_enum
);
static
const
struct
snd_soc_dapm_widget
nau8825_dapm_widgets
[]
=
{
SND_SOC_DAPM_AIF_OUT
(
"AIFTX"
,
"Capture"
,
0
,
NAU8825_REG_I2S_PCM_CTRL2
,
15
,
1
),
SND_SOC_DAPM_INPUT
(
"MIC"
),
SND_SOC_DAPM_MICBIAS
(
"MICBIAS"
,
NAU8825_REG_MIC_BIAS
,
8
,
0
),
SND_SOC_DAPM_PGA
(
"Frontend PGA"
,
NAU8825_REG_POWER_UP_CONTROL
,
14
,
0
,
NULL
,
0
),
SND_SOC_DAPM_ADC
(
"ADC"
,
NULL
,
NAU8825_REG_ENA_CTRL
,
8
,
0
),
SND_SOC_DAPM_SUPPLY
(
"ADC Clock"
,
NAU8825_REG_ENA_CTRL
,
7
,
0
,
NULL
,
0
),
SND_SOC_DAPM_SUPPLY
(
"ADC Power"
,
NAU8825_REG_ANALOG_ADC_2
,
6
,
0
,
NULL
,
0
),
/* ADC for button press detection */
SND_SOC_DAPM_ADC
(
"SAR"
,
NULL
,
NAU8825_REG_SAR_CTRL
,
NAU8825_SAR_ADC_EN_SFT
,
0
),
SND_SOC_DAPM_DAC
(
"ADACL"
,
NULL
,
NAU8825_REG_RDAC
,
12
,
0
),
SND_SOC_DAPM_DAC
(
"ADACR"
,
NULL
,
NAU8825_REG_RDAC
,
13
,
0
),
SND_SOC_DAPM_SUPPLY
(
"ADACL Clock"
,
NAU8825_REG_RDAC
,
8
,
0
,
NULL
,
0
),
SND_SOC_DAPM_SUPPLY
(
"ADACR Clock"
,
NAU8825_REG_RDAC
,
9
,
0
,
NULL
,
0
),
SND_SOC_DAPM_DAC
(
"DDACR"
,
NULL
,
NAU8825_REG_ENA_CTRL
,
NAU8825_ENABLE_DACR_SFT
,
0
),
SND_SOC_DAPM_DAC
(
"DDACL"
,
NULL
,
NAU8825_REG_ENA_CTRL
,
NAU8825_ENABLE_DACL_SFT
,
0
),
SND_SOC_DAPM_SUPPLY
(
"DDAC Clock"
,
NAU8825_REG_ENA_CTRL
,
6
,
0
,
NULL
,
0
),
SND_SOC_DAPM_MUX
(
"DACL Mux"
,
SND_SOC_NOPM
,
0
,
0
,
&
nau8825_dacl_mux
),
SND_SOC_DAPM_MUX
(
"DACR Mux"
,
SND_SOC_NOPM
,
0
,
0
,
&
nau8825_dacr_mux
),
SND_SOC_DAPM_PGA
(
"HP amp L"
,
NAU8825_REG_CLASSG_CTRL
,
1
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"HP amp R"
,
NAU8825_REG_CLASSG_CTRL
,
2
,
0
,
NULL
,
0
),
SND_SOC_DAPM_SUPPLY
(
"HP amp power"
,
NAU8825_REG_CLASSG_CTRL
,
0
,
0
,
NULL
,
0
),
SND_SOC_DAPM_SUPPLY
(
"Charge Pump"
,
NAU8825_REG_CHARGE_PUMP
,
5
,
0
,
nau8825_pump_event
,
SND_SOC_DAPM_POST_PMU
),
SND_SOC_DAPM_PGA
(
"Output Driver R Stage 1"
,
NAU8825_REG_POWER_UP_CONTROL
,
5
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"Output Driver L Stage 1"
,
NAU8825_REG_POWER_UP_CONTROL
,
4
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"Output Driver R Stage 2"
,
NAU8825_REG_POWER_UP_CONTROL
,
3
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA
(
"Output Driver L Stage 2"
,
NAU8825_REG_POWER_UP_CONTROL
,
2
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA_S
(
"Output Driver R Stage 3"
,
1
,
NAU8825_REG_POWER_UP_CONTROL
,
1
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA_S
(
"Output Driver L Stage 3"
,
1
,
NAU8825_REG_POWER_UP_CONTROL
,
0
,
0
,
NULL
,
0
),
SND_SOC_DAPM_PGA_S
(
"Output DACL"
,
2
,
NAU8825_REG_CHARGE_PUMP
,
8
,
1
,
NULL
,
0
),
SND_SOC_DAPM_PGA_S
(
"Output DACR"
,
2
,
NAU8825_REG_CHARGE_PUMP
,
9
,
1
,
NULL
,
0
),
SND_SOC_DAPM_OUTPUT
(
"HPOL"
),
SND_SOC_DAPM_OUTPUT
(
"HPOR"
),
};
static
const
struct
snd_soc_dapm_route
nau8825_dapm_routes
[]
=
{
{
"Frontend PGA"
,
NULL
,
"MIC"
},
{
"ADC"
,
NULL
,
"Frontend PGA"
},
{
"ADC"
,
NULL
,
"ADC Clock"
},
{
"ADC"
,
NULL
,
"ADC Power"
},
{
"AIFTX"
,
NULL
,
"ADC"
},
{
"DDACL"
,
NULL
,
"Playback"
},
{
"DDACR"
,
NULL
,
"Playback"
},
{
"DDACL"
,
NULL
,
"DDAC Clock"
},
{
"DDACR"
,
NULL
,
"DDAC Clock"
},
{
"DACL Mux"
,
"DACL"
,
"DDACL"
},
{
"DACL Mux"
,
"DACR"
,
"DDACR"
},
{
"DACR Mux"
,
"DACL"
,
"DDACL"
},
{
"DACR Mux"
,
"DACR"
,
"DDACR"
},
{
"HP amp L"
,
NULL
,
"DACL Mux"
},
{
"HP amp R"
,
NULL
,
"DACR Mux"
},
{
"HP amp L"
,
NULL
,
"HP amp power"
},
{
"HP amp R"
,
NULL
,
"HP amp power"
},
{
"ADACL"
,
NULL
,
"HP amp L"
},
{
"ADACR"
,
NULL
,
"HP amp R"
},
{
"ADACL"
,
NULL
,
"ADACL Clock"
},
{
"ADACR"
,
NULL
,
"ADACR Clock"
},
{
"Output Driver L Stage 1"
,
NULL
,
"ADACL"
},
{
"Output Driver R Stage 1"
,
NULL
,
"ADACR"
},
{
"Output Driver L Stage 2"
,
NULL
,
"Output Driver L Stage 1"
},
{
"Output Driver R Stage 2"
,
NULL
,
"Output Driver R Stage 1"
},
{
"Output Driver L Stage 3"
,
NULL
,
"Output Driver L Stage 2"
},
{
"Output Driver R Stage 3"
,
NULL
,
"Output Driver R Stage 2"
},
{
"Output DACL"
,
NULL
,
"Output Driver L Stage 3"
},
{
"Output DACR"
,
NULL
,
"Output Driver R Stage 3"
},
{
"HPOL"
,
NULL
,
"Output DACL"
},
{
"HPOR"
,
NULL
,
"Output DACR"
},
{
"HPOL"
,
NULL
,
"Charge Pump"
},
{
"HPOR"
,
NULL
,
"Charge Pump"
},
};
static
int
nau8825_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
;
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
int
val_len
=
0
;
switch
(
params_width
(
params
))
{
case
16
:
val_len
|=
NAU8825_I2S_DL_16
;
break
;
case
20
:
val_len
|=
NAU8825_I2S_DL_20
;
break
;
case
24
:
val_len
|=
NAU8825_I2S_DL_24
;
break
;
case
32
:
val_len
|=
NAU8825_I2S_DL_32
;
break
;
default:
return
-
EINVAL
;
}
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_I2S_PCM_CTRL1
,
NAU8825_I2S_DL_MASK
,
val_len
);
return
0
;
}
static
int
nau8825_set_dai_fmt
(
struct
snd_soc_dai
*
codec_dai
,
unsigned
int
fmt
)
{
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
int
ctrl1_val
=
0
,
ctrl2_val
=
0
;
switch
(
fmt
&
SND_SOC_DAIFMT_MASTER_MASK
)
{
case
SND_SOC_DAIFMT_CBM_CFM
:
ctrl2_val
|=
NAU8825_I2S_MS_MASTER
;
break
;
case
SND_SOC_DAIFMT_CBS_CFS
:
break
;
default:
return
-
EINVAL
;
}
switch
(
fmt
&
SND_SOC_DAIFMT_INV_MASK
)
{
case
SND_SOC_DAIFMT_NB_NF
:
break
;
case
SND_SOC_DAIFMT_IB_NF
:
ctrl1_val
|=
NAU8825_I2S_BP_INV
;
break
;
default:
return
-
EINVAL
;
}
switch
(
fmt
&
SND_SOC_DAIFMT_FORMAT_MASK
)
{
case
SND_SOC_DAIFMT_I2S
:
ctrl1_val
|=
NAU8825_I2S_DF_I2S
;
break
;
case
SND_SOC_DAIFMT_LEFT_J
:
ctrl1_val
|=
NAU8825_I2S_DF_LEFT
;
break
;
case
SND_SOC_DAIFMT_RIGHT_J
:
ctrl1_val
|=
NAU8825_I2S_DF_RIGTH
;
break
;
case
SND_SOC_DAIFMT_DSP_A
:
ctrl1_val
|=
NAU8825_I2S_DF_PCM_AB
;
break
;
case
SND_SOC_DAIFMT_DSP_B
:
ctrl1_val
|=
NAU8825_I2S_DF_PCM_AB
;
ctrl1_val
|=
NAU8825_I2S_PCMB_EN
;
break
;
default:
return
-
EINVAL
;
}
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_I2S_PCM_CTRL1
,
NAU8825_I2S_DL_MASK
|
NAU8825_I2S_DF_MASK
|
NAU8825_I2S_BP_MASK
|
NAU8825_I2S_PCMB_MASK
,
ctrl1_val
);
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_I2S_PCM_CTRL2
,
NAU8825_I2S_MS_MASK
,
ctrl2_val
);
return
0
;
}
static
const
struct
snd_soc_dai_ops
nau8825_dai_ops
=
{
.
hw_params
=
nau8825_hw_params
,
.
set_fmt
=
nau8825_set_dai_fmt
,
};
#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static
struct
snd_soc_dai_driver
nau8825_dai
=
{
.
name
=
"nau8825-hifi"
,
.
playback
=
{
.
stream_name
=
"Playback"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
NAU8825_RATES
,
.
formats
=
NAU8825_FORMATS
,
},
.
capture
=
{
.
stream_name
=
"Capture"
,
.
channels_min
=
1
,
.
channels_max
=
1
,
.
rates
=
NAU8825_RATES
,
.
formats
=
NAU8825_FORMATS
,
},
.
ops
=
&
nau8825_dai_ops
,
};
/**
* nau8825_enable_jack_detect - Specify a jack for event reporting
*
* @component: component to register the jack with
* @jack: jack to use to report headset and button events on
*
* After this function has been called the headset insert/remove and button
* events will be routed to the given jack. Jack can be null to stop
* reporting.
*/
int
nau8825_enable_jack_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
)
{
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
struct
regmap
*
regmap
=
nau8825
->
regmap
;
nau8825
->
jack
=
jack
;
/* Ground HP Outputs[1:0], needed for headset auto detection
* Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
*/
regmap_update_bits
(
regmap
,
NAU8825_REG_HSD_CTRL
,
NAU8825_HSD_AUTO_MODE
|
NAU8825_SPKR_DWN1R
|
NAU8825_SPKR_DWN1L
,
NAU8825_HSD_AUTO_MODE
|
NAU8825_SPKR_DWN1R
|
NAU8825_SPKR_DWN1L
);
regmap_update_bits
(
regmap
,
NAU8825_REG_INTERRUPT_MASK
,
NAU8825_IRQ_HEADSET_COMPLETE_EN
|
NAU8825_IRQ_EJECT_EN
,
0
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
nau8825_enable_jack_detect
);
static
bool
nau8825_is_jack_inserted
(
struct
regmap
*
regmap
)
{
int
status
;
regmap_read
(
regmap
,
NAU8825_REG_I2C_DEVICE_ID
,
&
status
);
return
!
(
status
&
NAU8825_GPIO2JD1
);
}
static
void
nau8825_restart_jack_detection
(
struct
regmap
*
regmap
)
{
/* this will restart the entire jack detection process including MIC/GND
* switching and create interrupts. We have to go from 0 to 1 and back
* to 0 to restart.
*/
regmap_update_bits
(
regmap
,
NAU8825_REG_JACK_DET_CTRL
,
NAU8825_JACK_DET_RESTART
,
NAU8825_JACK_DET_RESTART
);
regmap_update_bits
(
regmap
,
NAU8825_REG_JACK_DET_CTRL
,
NAU8825_JACK_DET_RESTART
,
0
);
}
static
void
nau8825_eject_jack
(
struct
nau8825
*
nau8825
)
{
struct
snd_soc_dapm_context
*
dapm
=
nau8825
->
dapm
;
struct
regmap
*
regmap
=
nau8825
->
regmap
;
snd_soc_dapm_disable_pin
(
dapm
,
"SAR"
);
snd_soc_dapm_disable_pin
(
dapm
,
"MICBIAS"
);
/* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
regmap_update_bits
(
regmap
,
NAU8825_REG_MIC_BIAS
,
NAU8825_MICBIAS_JKSLV
|
NAU8825_MICBIAS_JKR2
,
0
);
/* ground HPL/HPR, MICGRND1/2 */
regmap_update_bits
(
regmap
,
NAU8825_REG_HSD_CTRL
,
0xf
,
0xf
);
snd_soc_dapm_sync
(
dapm
);
}
static
int
nau8825_button_decode
(
int
value
)
{
int
buttons
=
0
;
/* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */
if
(
value
&
BIT
(
0
))
buttons
|=
SND_JACK_BTN_0
;
if
(
value
&
BIT
(
1
))
buttons
|=
SND_JACK_BTN_1
;
if
(
value
&
BIT
(
2
))
buttons
|=
SND_JACK_BTN_2
;
if
(
value
&
BIT
(
3
))
buttons
|=
SND_JACK_BTN_3
;
if
(
value
&
BIT
(
4
))
buttons
|=
SND_JACK_BTN_4
;
if
(
value
&
BIT
(
5
))
buttons
|=
SND_JACK_BTN_5
;
return
buttons
;
}
static
int
nau8825_jack_insert
(
struct
nau8825
*
nau8825
)
{
struct
regmap
*
regmap
=
nau8825
->
regmap
;
struct
snd_soc_dapm_context
*
dapm
=
nau8825
->
dapm
;
int
jack_status_reg
,
mic_detected
;
int
type
=
0
;
regmap_read
(
regmap
,
NAU8825_REG_GENERAL_STATUS
,
&
jack_status_reg
);
mic_detected
=
(
jack_status_reg
>>
10
)
&
3
;
switch
(
mic_detected
)
{
case
0
:
/* no mic */
type
=
SND_JACK_HEADPHONE
;
break
;
case
1
:
dev_dbg
(
nau8825
->
dev
,
"OMTP (micgnd1) mic connected
\n
"
);
type
=
SND_JACK_HEADSET
;
/* Unground MICGND1 */
regmap_update_bits
(
regmap
,
NAU8825_REG_HSD_CTRL
,
3
<<
2
,
1
<<
2
);
/* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
regmap_update_bits
(
regmap
,
NAU8825_REG_MIC_BIAS
,
NAU8825_MICBIAS_JKSLV
|
NAU8825_MICBIAS_JKR2
,
NAU8825_MICBIAS_JKR2
);
/* Attach SARADC to MICGND1 */
regmap_update_bits
(
regmap
,
NAU8825_REG_SAR_CTRL
,
NAU8825_SAR_INPUT_MASK
,
NAU8825_SAR_INPUT_JKR2
);
snd_soc_dapm_force_enable_pin
(
dapm
,
"MICBIAS"
);
snd_soc_dapm_force_enable_pin
(
dapm
,
"SAR"
);
snd_soc_dapm_sync
(
dapm
);
break
;
case
2
:
case
3
:
dev_dbg
(
nau8825
->
dev
,
"CTIA (micgnd2) mic connected
\n
"
);
type
=
SND_JACK_HEADSET
;
/* Unground MICGND2 */
regmap_update_bits
(
regmap
,
NAU8825_REG_HSD_CTRL
,
3
<<
2
,
2
<<
2
);
/* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
regmap_update_bits
(
regmap
,
NAU8825_REG_MIC_BIAS
,
NAU8825_MICBIAS_JKSLV
|
NAU8825_MICBIAS_JKR2
,
NAU8825_MICBIAS_JKSLV
);
/* Attach SARADC to MICGND2 */
regmap_update_bits
(
regmap
,
NAU8825_REG_SAR_CTRL
,
NAU8825_SAR_INPUT_MASK
,
NAU8825_SAR_INPUT_JKSLV
);
snd_soc_dapm_force_enable_pin
(
dapm
,
"MICBIAS"
);
snd_soc_dapm_force_enable_pin
(
dapm
,
"SAR"
);
snd_soc_dapm_sync
(
dapm
);
break
;
}
if
(
type
&
SND_JACK_HEADPHONE
)
{
/* Unground HPL/R */
regmap_update_bits
(
regmap
,
NAU8825_REG_HSD_CTRL
,
0x3
,
0
);
}
return
type
;
}
#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
SND_JACK_BTN_2 | SND_JACK_BTN_3)
static
irqreturn_t
nau8825_interrupt
(
int
irq
,
void
*
data
)
{
struct
nau8825
*
nau8825
=
(
struct
nau8825
*
)
data
;
struct
regmap
*
regmap
=
nau8825
->
regmap
;
int
active_irq
,
clear_irq
=
0
,
event
=
0
,
event_mask
=
0
;
regmap_read
(
regmap
,
NAU8825_REG_IRQ_STATUS
,
&
active_irq
);
if
((
active_irq
&
NAU8825_JACK_EJECTION_IRQ_MASK
)
==
NAU8825_JACK_EJECTION_DETECTED
)
{
nau8825_eject_jack
(
nau8825
);
event_mask
|=
SND_JACK_HEADSET
;
clear_irq
=
NAU8825_JACK_EJECTION_IRQ_MASK
;
}
else
if
(
active_irq
&
NAU8825_KEY_SHORT_PRESS_IRQ
)
{
int
key_status
;
regmap_read
(
regmap
,
NAU8825_REG_INT_CLR_KEY_STATUS
,
&
key_status
);
/* upper 8 bits of the register are for short pressed keys,
* lower 8 bits - for long pressed buttons
*/
nau8825
->
button_pressed
=
nau8825_button_decode
(
key_status
>>
8
);
event
|=
nau8825
->
button_pressed
;
event_mask
|=
NAU8825_BUTTONS
;
clear_irq
=
NAU8825_KEY_SHORT_PRESS_IRQ
;
}
else
if
(
active_irq
&
NAU8825_KEY_RELEASE_IRQ
)
{
event_mask
=
NAU8825_BUTTONS
;
clear_irq
=
NAU8825_KEY_RELEASE_IRQ
;
}
else
if
(
active_irq
&
NAU8825_HEADSET_COMPLETION_IRQ
)
{
if
(
nau8825_is_jack_inserted
(
regmap
))
{
event
|=
nau8825_jack_insert
(
nau8825
);
}
else
{
dev_warn
(
nau8825
->
dev
,
"Headset completion IRQ fired but no headset connected
\n
"
);
nau8825_eject_jack
(
nau8825
);
}
event_mask
|=
SND_JACK_HEADSET
;
clear_irq
=
NAU8825_HEADSET_COMPLETION_IRQ
;
}
if
(
!
clear_irq
)
clear_irq
=
active_irq
;
/* clears the rightmost interruption */
regmap_write
(
regmap
,
NAU8825_REG_INT_CLR_KEY_STATUS
,
clear_irq
);
if
(
event_mask
)
snd_soc_jack_report
(
nau8825
->
jack
,
event
,
event_mask
);
return
IRQ_HANDLED
;
}
static
void
nau8825_setup_buttons
(
struct
nau8825
*
nau8825
)
{
struct
regmap
*
regmap
=
nau8825
->
regmap
;
regmap_update_bits
(
regmap
,
NAU8825_REG_SAR_CTRL
,
NAU8825_SAR_TRACKING_GAIN_MASK
,
nau8825
->
sar_voltage
<<
NAU8825_SAR_TRACKING_GAIN_SFT
);
regmap_update_bits
(
regmap
,
NAU8825_REG_SAR_CTRL
,
NAU8825_SAR_COMPARE_TIME_MASK
,
nau8825
->
sar_compare_time
<<
NAU8825_SAR_COMPARE_TIME_SFT
);
regmap_update_bits
(
regmap
,
NAU8825_REG_SAR_CTRL
,
NAU8825_SAR_SAMPLING_TIME_MASK
,
nau8825
->
sar_sampling_time
<<
NAU8825_SAR_SAMPLING_TIME_SFT
);
regmap_update_bits
(
regmap
,
NAU8825_REG_KEYDET_CTRL
,
NAU8825_KEYDET_LEVELS_NR_MASK
,
(
nau8825
->
sar_threshold_num
-
1
)
<<
NAU8825_KEYDET_LEVELS_NR_SFT
);
regmap_update_bits
(
regmap
,
NAU8825_REG_KEYDET_CTRL
,
NAU8825_KEYDET_HYSTERESIS_MASK
,
nau8825
->
sar_hysteresis
<<
NAU8825_KEYDET_HYSTERESIS_SFT
);
regmap_update_bits
(
regmap
,
NAU8825_REG_KEYDET_CTRL
,
NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK
,
nau8825
->
key_debounce
<<
NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT
);
regmap_write
(
regmap
,
NAU8825_REG_VDET_THRESHOLD_1
,
(
nau8825
->
sar_threshold
[
0
]
<<
8
)
|
nau8825
->
sar_threshold
[
1
]);
regmap_write
(
regmap
,
NAU8825_REG_VDET_THRESHOLD_2
,
(
nau8825
->
sar_threshold
[
2
]
<<
8
)
|
nau8825
->
sar_threshold
[
3
]);
regmap_write
(
regmap
,
NAU8825_REG_VDET_THRESHOLD_3
,
(
nau8825
->
sar_threshold
[
4
]
<<
8
)
|
nau8825
->
sar_threshold
[
5
]);
regmap_write
(
regmap
,
NAU8825_REG_VDET_THRESHOLD_4
,
(
nau8825
->
sar_threshold
[
6
]
<<
8
)
|
nau8825
->
sar_threshold
[
7
]);
/* Enable short press and release interruptions */
regmap_update_bits
(
regmap
,
NAU8825_REG_INTERRUPT_MASK
,
NAU8825_IRQ_KEY_SHORT_PRESS_EN
|
NAU8825_IRQ_KEY_RELEASE_EN
,
0
);
}
static
void
nau8825_init_regs
(
struct
nau8825
*
nau8825
)
{
struct
regmap
*
regmap
=
nau8825
->
regmap
;
/* Enable Bias/Vmid */
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_BIAS_ADJ
,
NAU8825_BIAS_VMID
,
NAU8825_BIAS_VMID
);
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_BOOST
,
NAU8825_GLOBAL_BIAS_EN
,
NAU8825_GLOBAL_BIAS_EN
);
/* VMID Tieoff */
regmap_update_bits
(
regmap
,
NAU8825_REG_BIAS_ADJ
,
NAU8825_BIAS_VMID_SEL_MASK
,
nau8825
->
vref_impedance
<<
NAU8825_BIAS_VMID_SEL_SFT
);
/* Disable Boost Driver, Automatic Short circuit protection enable */
regmap_update_bits
(
regmap
,
NAU8825_REG_BOOST
,
NAU8825_PRECHARGE_DIS
|
NAU8825_HP_BOOST_G_DIS
|
NAU8825_SHORT_SHUTDOWN_EN
,
NAU8825_PRECHARGE_DIS
|
NAU8825_HP_BOOST_G_DIS
|
NAU8825_SHORT_SHUTDOWN_EN
);
regmap_update_bits
(
regmap
,
NAU8825_REG_GPIO12_CTRL
,
NAU8825_JKDET_OUTPUT_EN
,
nau8825
->
jkdet_enable
?
0
:
NAU8825_JKDET_OUTPUT_EN
);
regmap_update_bits
(
regmap
,
NAU8825_REG_GPIO12_CTRL
,
NAU8825_JKDET_PULL_EN
,
nau8825
->
jkdet_pull_enable
?
0
:
NAU8825_JKDET_PULL_EN
);
regmap_update_bits
(
regmap
,
NAU8825_REG_GPIO12_CTRL
,
NAU8825_JKDET_PULL_UP
,
nau8825
->
jkdet_pull_up
?
NAU8825_JKDET_PULL_UP
:
0
);
regmap_update_bits
(
regmap
,
NAU8825_REG_JACK_DET_CTRL
,
NAU8825_JACK_POLARITY
,
/* jkdet_polarity - 1 is for active-low */
nau8825
->
jkdet_polarity
?
0
:
NAU8825_JACK_POLARITY
);
regmap_update_bits
(
regmap
,
NAU8825_REG_JACK_DET_CTRL
,
NAU8825_JACK_INSERT_DEBOUNCE_MASK
,
nau8825
->
jack_insert_debounce
<<
NAU8825_JACK_INSERT_DEBOUNCE_SFT
);
regmap_update_bits
(
regmap
,
NAU8825_REG_JACK_DET_CTRL
,
NAU8825_JACK_EJECT_DEBOUNCE_MASK
,
nau8825
->
jack_eject_debounce
<<
NAU8825_JACK_EJECT_DEBOUNCE_SFT
);
/* Mask unneeded IRQs: 1 - disable, 0 - enable */
regmap_update_bits
(
regmap
,
NAU8825_REG_INTERRUPT_MASK
,
0x7ff
,
0x7ff
);
regmap_update_bits
(
regmap
,
NAU8825_REG_MIC_BIAS
,
NAU8825_MICBIAS_VOLTAGE_MASK
,
nau8825
->
micbias_voltage
);
if
(
nau8825
->
sar_threshold_num
)
nau8825_setup_buttons
(
nau8825
);
/* Default oversampling/decimations settings are unusable
* (audible hiss). Set it to something better.
*/
regmap_update_bits
(
regmap
,
NAU8825_REG_ADC_RATE
,
NAU8825_ADC_SYNC_DOWN_MASK
,
NAU8825_ADC_SYNC_DOWN_128
);
regmap_update_bits
(
regmap
,
NAU8825_REG_DAC_CTRL1
,
NAU8825_DAC_OVERSAMPLE_MASK
,
NAU8825_DAC_OVERSAMPLE_128
);
}
static
const
struct
regmap_config
nau8825_regmap_config
=
{
.
val_bits
=
16
,
.
reg_bits
=
16
,
.
max_register
=
NAU8825_REG_MAX
,
.
readable_reg
=
nau8825_readable_reg
,
.
writeable_reg
=
nau8825_writeable_reg
,
.
volatile_reg
=
nau8825_volatile_reg
,
.
cache_type
=
REGCACHE_RBTREE
,
.
reg_defaults
=
nau8825_reg_defaults
,
.
num_reg_defaults
=
ARRAY_SIZE
(
nau8825_reg_defaults
),
};
static
int
nau8825_codec_probe
(
struct
snd_soc_codec
*
codec
)
{
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
struct
snd_soc_dapm_context
*
dapm
=
snd_soc_codec_get_dapm
(
codec
);
nau8825
->
dapm
=
dapm
;
/* The interrupt clock is gated by x1[10:8],
* one of them needs to be enabled all the time for
* interrupts to happen.
*/
snd_soc_dapm_force_enable_pin
(
dapm
,
"DDACR"
);
snd_soc_dapm_sync
(
dapm
);
/* Unmask interruptions. Handler uses dapm object so we can enable
* interruptions only after dapm is fully initialized.
*/
regmap_write
(
nau8825
->
regmap
,
NAU8825_REG_INTERRUPT_DIS_CTRL
,
0
);
nau8825_restart_jack_detection
(
nau8825
->
regmap
);
return
0
;
}
/**
* nau8825_calc_fll_param - Calculate FLL parameters.
* @fll_in: external clock provided to codec.
* @fs: sampling rate.
* @fll_param: Pointer to structure of FLL parameters.
*
* Calculate FLL parameters to configure codec.
*
* Returns 0 for success or negative error code.
*/
static
int
nau8825_calc_fll_param
(
unsigned
int
fll_in
,
unsigned
int
fs
,
struct
nau8825_fll
*
fll_param
)
{
u64
fvco
;
unsigned
int
fref
,
i
;
/* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
* freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
* FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
*/
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
fll_pre_scalar
);
i
++
)
{
fref
=
fll_in
/
fll_pre_scalar
[
i
].
param
;
if
(
fref
<=
NAU_FREF_MAX
)
break
;
}
if
(
i
==
ARRAY_SIZE
(
fll_pre_scalar
))
return
-
EINVAL
;
fll_param
->
clk_ref_div
=
fll_pre_scalar
[
i
].
val
;
/* Choose the FLL ratio based on FREF */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
fll_ratio
);
i
++
)
{
if
(
fref
>=
fll_ratio
[
i
].
param
)
break
;
}
if
(
i
==
ARRAY_SIZE
(
fll_ratio
))
return
-
EINVAL
;
fll_param
->
ratio
=
fll_ratio
[
i
].
val
;
/* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
* FDCO must be within the 90MHz - 100MHz or the FFL cannot be
* guaranteed across the full range of operation.
* FDCO = freq_out * 2 * mclk_src_scaling
*/
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
mclk_src_scaling
);
i
++
)
{
fvco
=
256
*
fs
*
2
*
mclk_src_scaling
[
i
].
param
;
if
(
NAU_FVCO_MIN
<
fvco
&&
fvco
<
NAU_FVCO_MAX
)
break
;
}
if
(
i
==
ARRAY_SIZE
(
mclk_src_scaling
))
return
-
EINVAL
;
fll_param
->
mclk_src
=
mclk_src_scaling
[
i
].
val
;
/* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
* input based on FDCO, FREF and FLL ratio.
*/
fvco
=
div_u64
(
fvco
<<
16
,
fref
*
fll_param
->
ratio
);
fll_param
->
fll_int
=
(
fvco
>>
16
)
&
0x3FF
;
fll_param
->
fll_frac
=
fvco
&
0xFFFF
;
return
0
;
}
static
void
nau8825_fll_apply
(
struct
nau8825
*
nau8825
,
struct
nau8825_fll
*
fll_param
)
{
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_CLK_DIVIDER
,
NAU8825_CLK_MCLK_SRC_MASK
,
fll_param
->
mclk_src
);
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_FLL1
,
NAU8825_FLL_RATIO_MASK
,
fll_param
->
ratio
);
/* FLL 16-bit fractional input */
regmap_write
(
nau8825
->
regmap
,
NAU8825_REG_FLL2
,
fll_param
->
fll_frac
);
/* FLL 10-bit integer input */
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_FLL3
,
NAU8825_FLL_INTEGER_MASK
,
fll_param
->
fll_int
);
/* FLL pre-scaler */
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_FLL4
,
NAU8825_FLL_REF_DIV_MASK
,
fll_param
->
clk_ref_div
);
/* select divided VCO input */
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_FLL5
,
NAU8825_FLL_FILTER_SW_MASK
,
0x0000
);
/* FLL sigma delta modulator enable */
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_FLL6
,
NAU8825_SDM_EN_MASK
,
NAU8825_SDM_EN
);
}
/* freq_out must be 256*Fs in order to achieve the best performance */
static
int
nau8825_set_pll
(
struct
snd_soc_codec
*
codec
,
int
pll_id
,
int
source
,
unsigned
int
freq_in
,
unsigned
int
freq_out
)
{
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
struct
nau8825_fll
fll_param
;
int
ret
,
fs
;
fs
=
freq_out
/
256
;
ret
=
nau8825_calc_fll_param
(
freq_in
,
fs
,
&
fll_param
);
if
(
ret
<
0
)
{
dev_err
(
codec
->
dev
,
"Unsupported input clock %d
\n
"
,
freq_in
);
return
ret
;
}
dev_dbg
(
codec
->
dev
,
"mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x
\n
"
,
fll_param
.
mclk_src
,
fll_param
.
ratio
,
fll_param
.
fll_frac
,
fll_param
.
fll_int
,
fll_param
.
clk_ref_div
);
nau8825_fll_apply
(
nau8825
,
&
fll_param
);
mdelay
(
2
);
regmap_update_bits
(
nau8825
->
regmap
,
NAU8825_REG_CLK_DIVIDER
,
NAU8825_CLK_SRC_MASK
,
NAU8825_CLK_SRC_VCO
);
return
0
;
}
static
int
nau8825_configure_sysclk
(
struct
nau8825
*
nau8825
,
int
clk_id
,
unsigned
int
freq
)
{
struct
regmap
*
regmap
=
nau8825
->
regmap
;
int
ret
;
switch
(
clk_id
)
{
case
NAU8825_CLK_MCLK
:
regmap_update_bits
(
regmap
,
NAU8825_REG_CLK_DIVIDER
,
NAU8825_CLK_SRC_MASK
,
NAU8825_CLK_SRC_MCLK
);
regmap_update_bits
(
regmap
,
NAU8825_REG_FLL6
,
NAU8825_DCO_EN
,
0
);
/* We selected MCLK source but the clock itself managed externally */
if
(
!
nau8825
->
mclk
)
break
;
if
(
!
nau8825
->
mclk_freq
)
{
ret
=
clk_prepare_enable
(
nau8825
->
mclk
);
if
(
ret
)
{
dev_err
(
nau8825
->
dev
,
"Unable to prepare codec mclk
\n
"
);
return
ret
;
}
}
if
(
nau8825
->
mclk_freq
!=
freq
)
{
nau8825
->
mclk_freq
=
freq
;
freq
=
clk_round_rate
(
nau8825
->
mclk
,
freq
);
ret
=
clk_set_rate
(
nau8825
->
mclk
,
freq
);
if
(
ret
)
{
dev_err
(
nau8825
->
dev
,
"Unable to set mclk rate
\n
"
);
return
ret
;
}
}
break
;
case
NAU8825_CLK_INTERNAL
:
regmap_update_bits
(
regmap
,
NAU8825_REG_FLL6
,
NAU8825_DCO_EN
,
NAU8825_DCO_EN
);
regmap_update_bits
(
regmap
,
NAU8825_REG_CLK_DIVIDER
,
NAU8825_CLK_SRC_MASK
,
NAU8825_CLK_SRC_VCO
);
if
(
nau8825
->
mclk_freq
)
{
clk_disable_unprepare
(
nau8825
->
mclk
);
nau8825
->
mclk_freq
=
0
;
}
break
;
default:
dev_err
(
nau8825
->
dev
,
"Invalid clock id (%d)
\n
"
,
clk_id
);
return
-
EINVAL
;
}
dev_dbg
(
nau8825
->
dev
,
"Sysclk is %dHz and clock id is %d
\n
"
,
freq
,
clk_id
);
return
0
;
}
static
int
nau8825_set_sysclk
(
struct
snd_soc_codec
*
codec
,
int
clk_id
,
int
source
,
unsigned
int
freq
,
int
dir
)
{
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
return
nau8825_configure_sysclk
(
nau8825
,
clk_id
,
freq
);
}
static
int
nau8825_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
struct
nau8825
*
nau8825
=
snd_soc_codec_get_drvdata
(
codec
);
int
ret
;
switch
(
level
)
{
case
SND_SOC_BIAS_ON
:
break
;
case
SND_SOC_BIAS_PREPARE
:
break
;
case
SND_SOC_BIAS_STANDBY
:
if
(
snd_soc_codec_get_bias_level
(
codec
)
==
SND_SOC_BIAS_OFF
)
{
if
(
nau8825
->
mclk_freq
)
{
ret
=
clk_prepare_enable
(
nau8825
->
mclk
);
if
(
ret
)
{
dev_err
(
nau8825
->
dev
,
"Unable to prepare codec mclk
\n
"
);
return
ret
;
}
}
ret
=
regcache_sync
(
nau8825
->
regmap
);
if
(
ret
)
{
dev_err
(
codec
->
dev
,
"Failed to sync cache: %d
\n
"
,
ret
);
return
ret
;
}
}
break
;
case
SND_SOC_BIAS_OFF
:
if
(
nau8825
->
mclk_freq
)
clk_disable_unprepare
(
nau8825
->
mclk
);
regcache_mark_dirty
(
nau8825
->
regmap
);
break
;
}
return
0
;
}
static
struct
snd_soc_codec_driver
nau8825_codec_driver
=
{
.
probe
=
nau8825_codec_probe
,
.
set_sysclk
=
nau8825_set_sysclk
,
.
set_pll
=
nau8825_set_pll
,
.
set_bias_level
=
nau8825_set_bias_level
,
.
suspend_bias_off
=
true
,
.
controls
=
nau8825_controls
,
.
num_controls
=
ARRAY_SIZE
(
nau8825_controls
),
.
dapm_widgets
=
nau8825_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
nau8825_dapm_widgets
),
.
dapm_routes
=
nau8825_dapm_routes
,
.
num_dapm_routes
=
ARRAY_SIZE
(
nau8825_dapm_routes
),
};
static
void
nau8825_reset_chip
(
struct
regmap
*
regmap
)
{
regmap_write
(
regmap
,
NAU8825_REG_RESET
,
0x00
);
regmap_write
(
regmap
,
NAU8825_REG_RESET
,
0x00
);
}
static
void
nau8825_print_device_properties
(
struct
nau8825
*
nau8825
)
{
int
i
;
struct
device
*
dev
=
nau8825
->
dev
;
dev_dbg
(
dev
,
"jkdet-enable: %d
\n
"
,
nau8825
->
jkdet_enable
);
dev_dbg
(
dev
,
"jkdet-pull-enable: %d
\n
"
,
nau8825
->
jkdet_pull_enable
);
dev_dbg
(
dev
,
"jkdet-pull-up: %d
\n
"
,
nau8825
->
jkdet_pull_up
);
dev_dbg
(
dev
,
"jkdet-polarity: %d
\n
"
,
nau8825
->
jkdet_polarity
);
dev_dbg
(
dev
,
"micbias-voltage: %d
\n
"
,
nau8825
->
micbias_voltage
);
dev_dbg
(
dev
,
"vref-impedance: %d
\n
"
,
nau8825
->
vref_impedance
);
dev_dbg
(
dev
,
"sar-threshold-num: %d
\n
"
,
nau8825
->
sar_threshold_num
);
for
(
i
=
0
;
i
<
nau8825
->
sar_threshold_num
;
i
++
)
dev_dbg
(
dev
,
"sar-threshold[%d]=%d
\n
"
,
i
,
nau8825
->
sar_threshold
[
i
]);
dev_dbg
(
dev
,
"sar-hysteresis: %d
\n
"
,
nau8825
->
sar_hysteresis
);
dev_dbg
(
dev
,
"sar-voltage: %d
\n
"
,
nau8825
->
sar_voltage
);
dev_dbg
(
dev
,
"sar-compare-time: %d
\n
"
,
nau8825
->
sar_compare_time
);
dev_dbg
(
dev
,
"sar-sampling-time: %d
\n
"
,
nau8825
->
sar_sampling_time
);
dev_dbg
(
dev
,
"short-key-debounce: %d
\n
"
,
nau8825
->
key_debounce
);
dev_dbg
(
dev
,
"jack-insert-debounce: %d
\n
"
,
nau8825
->
jack_insert_debounce
);
dev_dbg
(
dev
,
"jack-eject-debounce: %d
\n
"
,
nau8825
->
jack_eject_debounce
);
}
static
int
nau8825_read_device_properties
(
struct
device
*
dev
,
struct
nau8825
*
nau8825
)
{
nau8825
->
jkdet_enable
=
device_property_read_bool
(
dev
,
"nuvoton,jkdet-enable"
);
nau8825
->
jkdet_pull_enable
=
device_property_read_bool
(
dev
,
"nuvoton,jkdet-pull-enable"
);
nau8825
->
jkdet_pull_up
=
device_property_read_bool
(
dev
,
"nuvoton,jkdet-pull-up"
);
device_property_read_u32
(
dev
,
"nuvoton,jkdet-polarity"
,
&
nau8825
->
jkdet_polarity
);
device_property_read_u32
(
dev
,
"nuvoton,micbias-voltage"
,
&
nau8825
->
micbias_voltage
);
device_property_read_u32
(
dev
,
"nuvoton,vref-impedance"
,
&
nau8825
->
vref_impedance
);
device_property_read_u32
(
dev
,
"nuvoton,sar-threshold-num"
,
&
nau8825
->
sar_threshold_num
);
device_property_read_u32_array
(
dev
,
"nuvoton,sar-threshold"
,
nau8825
->
sar_threshold
,
nau8825
->
sar_threshold_num
);
device_property_read_u32
(
dev
,
"nuvoton,sar-hysteresis"
,
&
nau8825
->
sar_hysteresis
);
device_property_read_u32
(
dev
,
"nuvoton,sar-voltage"
,
&
nau8825
->
sar_voltage
);
device_property_read_u32
(
dev
,
"nuvoton,sar-compare-time"
,
&
nau8825
->
sar_compare_time
);
device_property_read_u32
(
dev
,
"nuvoton,sar-sampling-time"
,
&
nau8825
->
sar_sampling_time
);
device_property_read_u32
(
dev
,
"nuvoton,short-key-debounce"
,
&
nau8825
->
key_debounce
);
device_property_read_u32
(
dev
,
"nuvoton,jack-insert-debounce"
,
&
nau8825
->
jack_insert_debounce
);
device_property_read_u32
(
dev
,
"nuvoton,jack-eject-debounce"
,
&
nau8825
->
jack_eject_debounce
);
nau8825
->
mclk
=
devm_clk_get
(
dev
,
"mclk"
);
if
(
PTR_ERR
(
nau8825
->
mclk
)
==
-
EPROBE_DEFER
)
{
return
-
EPROBE_DEFER
;
}
else
if
(
PTR_ERR
(
nau8825
->
mclk
)
==
-
ENOENT
)
{
/* The MCLK is managed externally or not used at all */
nau8825
->
mclk
=
NULL
;
dev_info
(
dev
,
"No 'mclk' clock found, assume MCLK is managed externally"
);
}
else
if
(
IS_ERR
(
nau8825
->
mclk
))
{
return
-
EINVAL
;
}
return
0
;
}
static
int
nau8825_setup_irq
(
struct
nau8825
*
nau8825
)
{
struct
regmap
*
regmap
=
nau8825
->
regmap
;
int
ret
;
/* IRQ Output Enable */
regmap_update_bits
(
regmap
,
NAU8825_REG_INTERRUPT_MASK
,
NAU8825_IRQ_OUTPUT_EN
,
NAU8825_IRQ_OUTPUT_EN
);
/* Enable internal VCO needed for interruptions */
nau8825_configure_sysclk
(
nau8825
,
NAU8825_CLK_INTERNAL
,
0
);
/* Enable DDACR needed for interrupts
* It is the same as force_enable_pin("DDACR") we do later
*/
regmap_update_bits
(
regmap
,
NAU8825_REG_ENA_CTRL
,
NAU8825_ENABLE_DACR
,
NAU8825_ENABLE_DACR
);
/* Chip needs one FSCLK cycle in order to generate interrupts,
* as we cannot guarantee one will be provided by the system. Turning
* master mode on then off enables us to generate that FSCLK cycle
* with a minimum of contention on the clock bus.
*/
regmap_update_bits
(
regmap
,
NAU8825_REG_I2S_PCM_CTRL2
,
NAU8825_I2S_MS_MASK
,
NAU8825_I2S_MS_MASTER
);
regmap_update_bits
(
regmap
,
NAU8825_REG_I2S_PCM_CTRL2
,
NAU8825_I2S_MS_MASK
,
NAU8825_I2S_MS_SLAVE
);
ret
=
devm_request_threaded_irq
(
nau8825
->
dev
,
nau8825
->
irq
,
NULL
,
nau8825_interrupt
,
IRQF_TRIGGER_LOW
|
IRQF_ONESHOT
,
"nau8825"
,
nau8825
);
if
(
ret
)
{
dev_err
(
nau8825
->
dev
,
"Cannot request irq %d (%d)
\n
"
,
nau8825
->
irq
,
ret
);
return
ret
;
}
return
0
;
}
static
int
nau8825_i2c_probe
(
struct
i2c_client
*
i2c
,
const
struct
i2c_device_id
*
id
)
{
struct
device
*
dev
=
&
i2c
->
dev
;
struct
nau8825
*
nau8825
=
dev_get_platdata
(
&
i2c
->
dev
);
int
ret
,
value
;
if
(
!
nau8825
)
{
nau8825
=
devm_kzalloc
(
dev
,
sizeof
(
*
nau8825
),
GFP_KERNEL
);
if
(
!
nau8825
)
return
-
ENOMEM
;
ret
=
nau8825_read_device_properties
(
dev
,
nau8825
);
if
(
ret
)
return
ret
;
}
i2c_set_clientdata
(
i2c
,
nau8825
);
nau8825
->
regmap
=
devm_regmap_init_i2c
(
i2c
,
&
nau8825_regmap_config
);
if
(
IS_ERR
(
nau8825
->
regmap
))
return
PTR_ERR
(
nau8825
->
regmap
);
nau8825
->
dev
=
dev
;
nau8825
->
irq
=
i2c
->
irq
;
nau8825_print_device_properties
(
nau8825
);
nau8825_reset_chip
(
nau8825
->
regmap
);
ret
=
regmap_read
(
nau8825
->
regmap
,
NAU8825_REG_I2C_DEVICE_ID
,
&
value
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"Failed to read device id from the NAU8825: %d
\n
"
,
ret
);
return
ret
;
}
if
((
value
&
NAU8825_SOFTWARE_ID_MASK
)
!=
NAU8825_SOFTWARE_ID_NAU8825
)
{
dev_err
(
dev
,
"Not a NAU8825 chip
\n
"
);
return
-
ENODEV
;
}
nau8825_init_regs
(
nau8825
);
if
(
i2c
->
irq
)
nau8825_setup_irq
(
nau8825
);
return
snd_soc_register_codec
(
&
i2c
->
dev
,
&
nau8825_codec_driver
,
&
nau8825_dai
,
1
);
}
static
int
nau8825_i2c_remove
(
struct
i2c_client
*
client
)
{
snd_soc_unregister_codec
(
&
client
->
dev
);
return
0
;
}
static
const
struct
i2c_device_id
nau8825_i2c_ids
[]
=
{
{
"nau8825"
,
0
},
{
}
};
#ifdef CONFIG_OF
static
const
struct
of_device_id
nau8825_of_ids
[]
=
{
{
.
compatible
=
"nuvoton,nau8825"
,
},
{}
};
MODULE_DEVICE_TABLE
(
of
,
nau8825_of_ids
);
#endif
#ifdef CONFIG_ACPI
static
const
struct
acpi_device_id
nau8825_acpi_match
[]
=
{
{
"10508825"
,
0
},
{},
};
MODULE_DEVICE_TABLE
(
acpi
,
nau8825_acpi_match
);
#endif
static
struct
i2c_driver
nau8825_driver
=
{
.
driver
=
{
.
name
=
"nau8825"
,
.
of_match_table
=
of_match_ptr
(
nau8825_of_ids
),
.
acpi_match_table
=
ACPI_PTR
(
nau8825_acpi_match
),
},
.
probe
=
nau8825_i2c_probe
,
.
remove
=
nau8825_i2c_remove
,
.
id_table
=
nau8825_i2c_ids
,
};
module_i2c_driver
(
nau8825_driver
);
MODULE_DESCRIPTION
(
"ASoC nau8825 driver"
);
MODULE_AUTHOR
(
"Anatol Pomozov <anatol@chromium.org>"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/codecs/nau8825.h
0 → 100644
View file @
3db5de56
/*
* NAU8825 ALSA SoC audio driver
*
* Copyright 2015 Google Inc.
* Author: Anatol Pomozov <anatol.pomozov@chrominium.org>
*
* 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 __NAU8825_H__
#define __NAU8825_H__
#define NAU8825_REG_RESET 0x00
#define NAU8825_REG_ENA_CTRL 0x01
#define NAU8825_REG_CLK_DIVIDER 0x03
#define NAU8825_REG_FLL1 0x04
#define NAU8825_REG_FLL2 0x05
#define NAU8825_REG_FLL3 0x06
#define NAU8825_REG_FLL4 0x07
#define NAU8825_REG_FLL5 0x08
#define NAU8825_REG_FLL6 0x09
#define NAU8825_REG_FLL_VCO_RSV 0x0a
#define NAU8825_REG_HSD_CTRL 0x0c
#define NAU8825_REG_JACK_DET_CTRL 0x0d
#define NAU8825_REG_INTERRUPT_MASK 0x0f
#define NAU8825_REG_IRQ_STATUS 0x10
#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11
#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12
#define NAU8825_REG_SAR_CTRL 0x13
#define NAU8825_REG_KEYDET_CTRL 0x14
#define NAU8825_REG_VDET_THRESHOLD_1 0x15
#define NAU8825_REG_VDET_THRESHOLD_2 0x16
#define NAU8825_REG_VDET_THRESHOLD_3 0x17
#define NAU8825_REG_VDET_THRESHOLD_4 0x18
#define NAU8825_REG_GPIO34_CTRL 0x19
#define NAU8825_REG_GPIO12_CTRL 0x1a
#define NAU8825_REG_TDM_CTRL 0x1b
#define NAU8825_REG_I2S_PCM_CTRL1 0x1c
#define NAU8825_REG_I2S_PCM_CTRL2 0x1d
#define NAU8825_REG_LEFT_TIME_SLOT 0x1e
#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f
#define NAU8825_REG_BIQ_CTRL 0x20
#define NAU8825_REG_BIQ_COF1 0x21
#define NAU8825_REG_BIQ_COF2 0x22
#define NAU8825_REG_BIQ_COF3 0x23
#define NAU8825_REG_BIQ_COF4 0x24
#define NAU8825_REG_BIQ_COF5 0x25
#define NAU8825_REG_BIQ_COF6 0x26
#define NAU8825_REG_BIQ_COF7 0x27
#define NAU8825_REG_BIQ_COF8 0x28
#define NAU8825_REG_BIQ_COF9 0x29
#define NAU8825_REG_BIQ_COF10 0x2a
#define NAU8825_REG_ADC_RATE 0x2b
#define NAU8825_REG_DAC_CTRL1 0x2c
#define NAU8825_REG_DAC_CTRL2 0x2d
#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f
#define NAU8825_REG_ADC_DGAIN_CTRL 0x30
#define NAU8825_REG_MUTE_CTRL 0x31
#define NAU8825_REG_HSVOL_CTRL 0x32
#define NAU8825_REG_DACL_CTRL 0x33
#define NAU8825_REG_DACR_CTRL 0x34
#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38
#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39
#define NAU8825_REG_ADC_DRC_SLOPES 0x3a
#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b
#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45
#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46
#define NAU8825_REG_DAC_DRC_SLOPES 0x47
#define NAU8825_REG_DAC_DRC_ATKDCY 0x48
#define NAU8825_REG_IMM_MODE_CTRL 0x4c
#define NAU8825_REG_IMM_RMS_L 0x4d
#define NAU8825_REG_IMM_RMS_R 0x4e
#define NAU8825_REG_CLASSG_CTRL 0x50
#define NAU8825_REG_OPT_EFUSE_CTRL 0x51
#define NAU8825_REG_MISC_CTRL 0x55
#define NAU8825_REG_I2C_DEVICE_ID 0x58
#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59
#define NAU8825_REG_BIAS_ADJ 0x66
#define NAU8825_REG_TRIM_SETTINGS 0x68
#define NAU8825_REG_ANALOG_CONTROL_1 0x69
#define NAU8825_REG_ANALOG_CONTROL_2 0x6a
#define NAU8825_REG_ANALOG_ADC_1 0x71
#define NAU8825_REG_ANALOG_ADC_2 0x72
#define NAU8825_REG_RDAC 0x73
#define NAU8825_REG_MIC_BIAS 0x74
#define NAU8825_REG_BOOST 0x76
#define NAU8825_REG_FEPGA 0x77
#define NAU8825_REG_POWER_UP_CONTROL 0x7f
#define NAU8825_REG_CHARGE_PUMP 0x80
#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
#define NAU8825_REG_GENERAL_STATUS 0x82
#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
/* ENA_CTRL (0x1) */
#define NAU8825_ENABLE_DACR_SFT 10
#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
#define NAU8825_ENABLE_DACL_SFT 9
#define NAU8825_ENABLE_ADC_SFT 8
#define NAU8825_ENABLE_SAR_SFT 1
/* CLK_DIVIDER (0x3) */
#define NAU8825_CLK_SRC_SFT 15
#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT)
#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT)
#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT)
#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0)
/* FLL1 (0x04) */
#define NAU8825_FLL_RATIO_MASK (0x7f << 0)
/* FLL3 (0x06) */
#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
/* FLL4 (0x07) */
#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
/* FLL5 (0x08) */
#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
/* FLL6 (0x9) */
#define NAU8825_DCO_EN_MASK (0x1 << 15)
#define NAU8825_DCO_EN (0x1 << 15)
#define NAU8825_DCO_DIS (0x0 << 15)
#define NAU8825_SDM_EN_MASK (0x1 << 14)
#define NAU8825_SDM_EN (0x1 << 14)
#define NAU8825_SDM_DIS (0x0 << 14)
/* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6)
/* 0 - short to GND, 1 - open */
#define NAU8825_SPKR_DWN1R (1 << 1)
#define NAU8825_SPKR_DWN1L (1 << 0)
/* JACK_DET_CTRL (0xd) */
#define NAU8825_JACK_DET_RESTART (1 << 9)
#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT)
#define NAU8825_JACK_POLARITY (1 << 1)
/* 0 - active low, 1 - active high */
/* INTERRUPT_MASK (0xf) */
#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
#define NAU8825_IRQ_EJECT_EN (1 << 2)
/* IRQ_STATUS (0x10) */
#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9)
#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8)
#define NAU8825_KEY_IRQ_MASK (0x7 << 5)
#define NAU8825_KEY_RELEASE_IRQ (1 << 7)
#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6)
#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5)
#define NAU8825_MIC_DETECTION_IRQ (1 << 4)
#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2)
#define NAU8825_JACK_EJECTION_DETECTED (1 << 2)
#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0)
#define NAU8825_JACK_INSERTION_DETECTED (1 << 0)
/* INTERRUPT_DIS_CTRL (0x12) */
#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10)
#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
#define NAU8825_IRQ_EJECT_DIS (1 << 2)
/* SAR_CTRL (0x13) */
#define NAU8825_SAR_ADC_EN_SFT 12
#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT)
#define NAU8825_SAR_INPUT_MASK (1 << 11)
#define NAU8825_SAR_INPUT_JKSLV (1 << 11)
#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
#define NAU8825_SAR_TRACKING_GAIN_SFT 8
#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
#define NAU8825_SAR_COMPARE_TIME_SFT 2
#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2)
#define NAU8825_SAR_SAMPLING_TIME_SFT 0
#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0)
/* KEYDET_CTRL (0x14) */
#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12
#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT)
#define NAU8825_KEYDET_LEVELS_NR_SFT 8
#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8)
#define NAU8825_KEYDET_HYSTERESIS_SFT 0
#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf
/* GPIO12_CTRL (0x1a) */
#define NAU8825_JKDET_PULL_UP (1 << 11)
/* 0 - pull down, 1 - pull up */
#define NAU8825_JKDET_PULL_EN (1 << 9)
/* 0 - enable pull, 1 - disable */
#define NAU8825_JKDET_OUTPUT_EN (1 << 8)
/* 0 - enable input, 1 - enable output */
/* I2S_PCM_CTRL1 (0x1c) */
#define NAU8825_I2S_BP_SFT 7
#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT)
#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT)
#define NAU8825_I2S_PCMB_SFT 6
#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT)
#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT)
#define NAU8825_I2S_DL_SFT 2
#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT)
#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT)
#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT)
#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT)
#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT)
#define NAU8825_I2S_DF_SFT 0
#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT)
#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT)
#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT)
#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT)
#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT)
/* I2S_PCM_CTRL2 (0x1d) */
#define NAU8825_I2S_TRISTATE (1 << 15)
/* 0 - normal mode, 1 - Hi-Z output */
#define NAU8825_I2S_MS_SFT 3
#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
/* ADC_RATE (0x2b) */
#define NAU8825_ADC_SYNC_DOWN_SFT 0
#define NAU8825_ADC_SYNC_DOWN_MASK 0x3
#define NAU8825_ADC_SYNC_DOWN_32 0
#define NAU8825_ADC_SYNC_DOWN_64 1
#define NAU8825_ADC_SYNC_DOWN_128 2
#define NAU8825_ADC_SYNC_DOWN_256 3
/* DAC_CTRL1 (0x2c) */
#define NAU8825_DAC_CLIP_OFF (1 << 7)
#define NAU8825_DAC_OVERSAMPLE_SFT 0
#define NAU8825_DAC_OVERSAMPLE_MASK 0x7
#define NAU8825_DAC_OVERSAMPLE_64 0
#define NAU8825_DAC_OVERSAMPLE_256 1
#define NAU8825_DAC_OVERSAMPLE_128 2
#define NAU8825_DAC_OVERSAMPLE_32 4
/* MUTE_CTRL (0x31) */
#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
#define NAU8825_DAC_SOFT_MUTE (1 << 9)
/* HSVOL_CTRL (0x32) */
#define NAU8825_HP_MUTE (1 << 15)
/* DACL_CTRL (0x33) */
#define NAU8825_DACL_CH_SEL_SFT 9
/* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9
/* I2C_DEVICE_ID (0x58) */
#define NAU8825_GPIO2JD1 (1 << 7)
#define NAU8825_SOFTWARE_ID_MASK 0x3
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */
#define NAU8825_BIAS_VMID (1 << 6)
#define NAU8825_BIAS_VMID_SEL_SFT 4
#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
/* ANALOG_CONTROL_2 (0x6a) */
#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
#define NAU8825_DAC_CAPACITOR_LSB (1 << 0)
/* ANALOG_ADC_2 (0x72) */
#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8)
#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8)
#define NAU8825_ADC_VREFSEL_VMID (1 << 8)
#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8)
#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
#define NAU8825_POWERUP_ADCL (1 << 6)
/* MIC_BIAS (0x74) */
#define NAU8825_MICBIAS_JKSLV (1 << 14)
#define NAU8825_MICBIAS_JKR2 (1 << 12)
#define NAU8825_MICBIAS_POWERUP_SFT 8
#define NAU8825_MICBIAS_VOLTAGE_SFT 0
#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7
/* BOOST (0x76) */
#define NAU8825_PRECHARGE_DIS (1 << 13)
#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
#define NAU8825_HP_BOOST_G_DIS (1 << 8)
#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
/* POWER_UP_CONTROL (0x7f) */
#define NAU8825_POWERUP_INTEGR_R (1 << 5)
#define NAU8825_POWERUP_INTEGR_L (1 << 4)
#define NAU8825_POWERUP_DRV_IN_R (1 << 3)
#define NAU8825_POWERUP_DRV_IN_L (1 << 2)
#define NAU8825_POWERUP_HP_DRV_R (1 << 1)
#define NAU8825_POWERUP_HP_DRV_L (1 << 0)
/* CHARGE_PUMP (0x80) */
#define NAU8825_JAMNODCLOW (1 << 10)
#define NAU8825_POWER_DOWN_DACR (1 << 9)
#define NAU8825_POWER_DOWN_DACL (1 << 8)
#define NAU8825_CHANRGE_PUMP_EN (1 << 5)
/* System Clock Source */
enum
{
NAU8825_CLK_MCLK
=
0
,
NAU8825_CLK_INTERNAL
,
};
struct
nau8825
{
struct
device
*
dev
;
struct
regmap
*
regmap
;
struct
snd_soc_dapm_context
*
dapm
;
struct
snd_soc_jack
*
jack
;
struct
clk
*
mclk
;
int
irq
;
int
mclk_freq
;
/* 0 - mclk is disabled */
int
button_pressed
;
int
micbias_voltage
;
int
vref_impedance
;
bool
jkdet_enable
;
bool
jkdet_pull_enable
;
bool
jkdet_pull_up
;
int
jkdet_polarity
;
int
sar_threshold_num
;
int
sar_threshold
[
8
];
int
sar_hysteresis
;
int
sar_voltage
;
int
sar_compare_time
;
int
sar_sampling_time
;
int
key_debounce
;
int
jack_insert_debounce
;
int
jack_eject_debounce
;
};
int
nau8825_enable_jack_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
);
#endif
/* __NAU8825_H__ */
sound/soc/pxa/pxa2xx-ac97.c
View file @
3db5de56
...
...
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
...
...
@@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
.
reset
=
pxa2xx_ac97_cold_reset
,
};
static
unsigned
long
pxa2xx_ac97_pcm_stereo_in_req
=
11
;
static
struct
pxad_param
pxa2xx_ac97_pcm_stereo_in_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
11
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_stereo_in
=
{
.
addr
=
__PREG
(
PCDR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_4_BYTES
,
...
...
@@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
.
filter_data
=
&
pxa2xx_ac97_pcm_stereo_in_req
,
};
static
unsigned
long
pxa2xx_ac97_pcm_stereo_out_req
=
12
;
static
struct
pxad_param
pxa2xx_ac97_pcm_stereo_out_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
12
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_stereo_out
=
{
.
addr
=
__PREG
(
PCDR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_4_BYTES
,
...
...
@@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
.
filter_data
=
&
pxa2xx_ac97_pcm_stereo_out_req
,
};
static
unsigned
long
pxa2xx_ac97_pcm_aux_mono_out_req
=
10
;
static
struct
pxad_param
pxa2xx_ac97_pcm_aux_mono_out_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
10
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_aux_mono_out
=
{
.
addr
=
__PREG
(
MODR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_2_BYTES
,
...
...
@@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
.
filter_data
=
&
pxa2xx_ac97_pcm_aux_mono_out_req
,
};
static
unsigned
long
pxa2xx_ac97_pcm_aux_mono_in_req
=
9
;
static
struct
pxad_param
pxa2xx_ac97_pcm_aux_mono_in_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
9
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_aux_mono_in
=
{
.
addr
=
__PREG
(
MODR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_2_BYTES
,
...
...
@@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
.
filter_data
=
&
pxa2xx_ac97_pcm_aux_mono_in_req
,
};
static
unsigned
long
pxa2xx_ac97_pcm_aux_mic_mono_req
=
8
;
static
struct
pxad_param
pxa2xx_ac97_pcm_aux_mic_mono_req
=
{
.
prio
=
PXAD_PRIO_LOWEST
,
.
drcmr
=
8
,
};
static
struct
snd_dmaengine_dai_dma_data
pxa2xx_ac97_pcm_mic_mono_in
=
{
.
addr
=
__PREG
(
MCDR
),
.
addr_width
=
DMA_SLAVE_BUSWIDTH_2_BYTES
,
...
...
@@ -89,8 +107,7 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
.
filter_data
=
&
pxa2xx_ac97_pcm_aux_mic_mono_req
,
};
static
int
pxa2xx_ac97_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
static
int
pxa2xx_ac97_hifi_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
cpu_dai
)
{
struct
snd_dmaengine_dai_dma_data
*
dma_data
;
...
...
@@ -105,8 +122,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
return
0
;
}
static
int
pxa2xx_ac97_hw_aux_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
static
int
pxa2xx_ac97_aux_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
cpu_dai
)
{
struct
snd_dmaengine_dai_dma_data
*
dma_data
;
...
...
@@ -121,8 +137,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
return
0
;
}
static
int
pxa2xx_ac97_hw_mic_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
static
int
pxa2xx_ac97_mic_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
cpu_dai
)
{
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
...
...
@@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_48000)
static
const
struct
snd_soc_dai_ops
pxa_ac97_hifi_dai_ops
=
{
.
hw_params
=
pxa2xx_ac97_hw_params
,
.
startup
=
pxa2xx_ac97_hifi_startup
,
};
static
const
struct
snd_soc_dai_ops
pxa_ac97_aux_dai_ops
=
{
.
hw_params
=
pxa2xx_ac97_hw_aux_params
,
.
startup
=
pxa2xx_ac97_aux_startup
,
};
static
const
struct
snd_soc_dai_ops
pxa_ac97_mic_dai_ops
=
{
.
hw_params
=
pxa2xx_ac97_hw_mic_params
,
.
startup
=
pxa2xx_ac97_mic_startup
,
};
/*
...
...
sound/soc/pxa/pxa2xx-i2s.c
View file @
3db5de56
...
...
@@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
/* Along with FIFO servicing */
SAIMR
&=
~
(
SAIMR_RFS
|
SAIMR_TFS
);
snd_soc_dai_init_dma_data
(
dai
,
&
pxa2xx_i2s_pcm_stereo_out
,
&
pxa2xx_i2s_pcm_stereo_in
);
return
0
;
}
...
...
sound/soc/pxa/pxa2xx-pcm.c
View file @
3db5de56
...
...
@@ -15,8 +15,6 @@
#include <linux/dmaengine.h>
#include <linux/of.h>
#include <mach/dma.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
...
...
@@ -27,11 +25,8 @@
static
int
pxa2xx_pcm_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
pxa2xx_runtime_data
*
prtd
=
runtime
->
private_data
;
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
snd_dmaengine_dai_dma_data
*
dma
;
int
ret
;
dma
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
...
...
@@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
if
(
!
dma
)
return
0
;
/* this may get called several times by oss emulation
* with different params */
if
(
prtd
->
params
==
NULL
)
{
prtd
->
params
=
dma
;
ret
=
pxa_request_dma
(
"name"
,
DMA_PRIO_LOW
,
pxa2xx_pcm_dma_irq
,
substream
);
if
(
ret
<
0
)
return
ret
;
prtd
->
dma_ch
=
ret
;
}
else
if
(
prtd
->
params
!=
dma
)
{
pxa_free_dma
(
prtd
->
dma_ch
);
prtd
->
params
=
dma
;
ret
=
pxa_request_dma
(
"name"
,
DMA_PRIO_LOW
,
pxa2xx_pcm_dma_irq
,
substream
);
if
(
ret
<
0
)
return
ret
;
prtd
->
dma_ch
=
ret
;
}
return
__pxa2xx_pcm_hw_params
(
substream
,
params
);
}
static
int
pxa2xx_pcm_hw_free
(
struct
snd_pcm_substream
*
substream
)
{
struct
pxa2xx_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
__pxa2xx_pcm_hw_free
(
substream
);
if
(
prtd
->
dma_ch
>=
0
)
{
pxa_free_dma
(
prtd
->
dma_ch
);
prtd
->
dma_ch
=
-
1
;
prtd
->
params
=
NULL
;
}
return
0
;
}
...
...
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