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
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
Hide 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
;
return
ret
;
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,9 +107,8 @@ 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
,
struct
snd_soc_dai
*
cpu_dai
)
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,9 +122,8 @@ 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
,
struct
snd_soc_dai
*
cpu_dai
)
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,9 +137,8 @@ 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
,
struct
snd_soc_dai
*
cpu_dai
)
static
int
pxa2xx_ac97_mic_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
cpu_dai
)
{
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
return
-
ENODEV
;
...
...
@@ -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