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
56b3f31f
Commit
56b3f31f
authored
Feb 11, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/tegra' into asoc-next
parents
6ee00c16
ec05cc55
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
918 additions
and
4 deletions
+918
-4
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
...n/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
+51
-0
Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
...ntation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
+22
-0
sound/soc/tegra/Kconfig
sound/soc/tegra/Kconfig
+19
-0
sound/soc/tegra/Makefile
sound/soc/tegra/Makefile
+4
-0
sound/soc/tegra/tegra20_ac97.c
sound/soc/tegra/tegra20_ac97.c
+480
-0
sound/soc/tegra/tegra20_ac97.h
sound/soc/tegra/tegra20_ac97.h
+95
-0
sound/soc/tegra/tegra20_das.c
sound/soc/tegra/tegra20_das.c
+13
-0
sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_ahub.c
+2
-2
sound/soc/tegra/tegra30_i2s.c
sound/soc/tegra/tegra30_i2s.c
+2
-2
sound/soc/tegra/tegra_asoc_utils.c
sound/soc/tegra/tegra_asoc_utils.c
+53
-0
sound/soc/tegra/tegra_asoc_utils.h
sound/soc/tegra/tegra_asoc_utils.h
+1
-0
sound/soc/tegra/tegra_wm9712.c
sound/soc/tegra/tegra_wm9712.c
+176
-0
No files found.
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
0 → 100644
View file @
56b3f31f
NVIDIA Tegra audio complex
Required properties:
- compatible : "nvidia,tegra-audio-wm9712"
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources and
sinks are the WM9712's pins, and the jacks on the board:
WM9712 pins:
* MONOOUT
* HPOUTL
* HPOUTR
* LOUT2
* ROUT2
* OUT3
* LINEINL
* LINEINR
* PHONE
* PCBEEP
* MIC1
* MIC2
* Mic Bias
Board connectors:
* Headphone
* LineIn
* Mic
- nvidia,ac97-controller : The phandle of the Tegra AC97 controller
Example:
sound {
compatible = "nvidia,tegra-audio-wm9712-colibri_t20",
"nvidia,tegra-audio-wm9712";
nvidia,model = "Toradex Colibri T20";
nvidia,audio-routing =
"Headphone", "HPOUTL",
"Headphone", "HPOUTR",
"LineIn", "LINEINL",
"LineIn", "LINEINR",
"Mic", "MIC1";
nvidia,ac97-controller = <&ac97>;
};
Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
0 → 100644
View file @
56b3f31f
NVIDIA Tegra 20 AC97 controller
Required properties:
- compatible : "nvidia,tegra20-ac97"
- reg : Should contain AC97 controller registers location and length
- interrupts : Should contain AC97 interrupt
- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
request selector for the AC97 controller
- nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number
of the GPIO used to reset the external AC97 codec
- nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number
of the GPIO corresponding with the AC97 DAP _FS line
Example:
ac97@70002000 {
compatible = "nvidia,tegra20-ac97";
reg = <0x70002000 0x200>;
interrupts = <0 81 0x04>;
nvidia,dma-request-selector = <&apbdma 12>;
nvidia,codec-reset-gpio = <&gpio 170 0>;
nvidia,codec-sync-gpio = <&gpio 120 0>;
};
sound/soc/tegra/Kconfig
View file @
56b3f31f
...
...
@@ -6,6 +6,16 @@ config SND_SOC_TEGRA
help
Say Y or M here if you want support for SoC audio on Tegra.
config SND_SOC_TEGRA20_AC97
tristate
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
select SND_SOC_AC97_BUS
select SND_SOC_TEGRA20_DAS
help
Say Y or M if you want to add support for codecs attached to the
Tegra20 AC97 interface. You will also need to select the individual
machine drivers to support below.
config SND_SOC_TEGRA20_DAS
tristate
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
...
...
@@ -70,6 +80,15 @@ config SND_SOC_TEGRA_WM8903
boards using the WM8093 codec. Currently, the supported boards are
Harmony, Ventana, Seaboard, Kaen, and Aebl.
config SND_SOC_TEGRA_WM9712
tristate "SoC Audio support for Tegra boards using a WM9712 codec"
depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
select SND_SOC_TEGRA20_AC97
select SND_SOC_WM9712
help
Say Y or M here if you want to add support for SoC audio on Tegra
boards using the WM9712 (or compatible) codec.
config SND_SOC_TEGRA_TRIMSLICE
tristate "SoC Audio support for TrimSlice board"
depends on SND_SOC_TEGRA && I2C
...
...
sound/soc/tegra/Makefile
View file @
56b3f31f
# Tegra platform Support
snd-soc-tegra-pcm-objs
:=
tegra_pcm.o
snd-soc-tegra-utils-objs
+=
tegra_asoc_utils.o
snd-soc-tegra20-ac97-objs
:=
tegra20_ac97.o
snd-soc-tegra20-das-objs
:=
tegra20_das.o
snd-soc-tegra20-i2s-objs
:=
tegra20_i2s.o
snd-soc-tegra20-spdif-objs
:=
tegra20_spdif.o
...
...
@@ -9,6 +10,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o
obj-$(CONFIG_SND_SOC_TEGRA)
+=
snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA)
+=
snd-soc-tegra-utils.o
obj-$(CONFIG_SND_SOC_TEGRA20_AC97)
+=
snd-soc-tegra20-ac97.o
obj-$(CONFIG_SND_SOC_TEGRA20_DAS)
+=
snd-soc-tegra20-das.o
obj-$(CONFIG_SND_SOC_TEGRA20_I2S)
+=
snd-soc-tegra20-i2s.o
obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF)
+=
snd-soc-tegra20-spdif.o
...
...
@@ -18,10 +20,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
# Tegra machine Support
snd-soc-tegra-wm8753-objs
:=
tegra_wm8753.o
snd-soc-tegra-wm8903-objs
:=
tegra_wm8903.o
snd-soc-tegra-wm9712-objs
:=
tegra_wm9712.o
snd-soc-tegra-trimslice-objs
:=
trimslice.o
snd-soc-tegra-alc5632-objs
:=
tegra_alc5632.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8753)
+=
snd-soc-tegra-wm8753.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903)
+=
snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_WM9712)
+=
snd-soc-tegra-wm9712.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE)
+=
snd-soc-tegra-trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632)
+=
snd-soc-tegra-alc5632.o
sound/soc/tegra/tegra20_ac97.c
0 → 100644
View file @
56b3f31f
/*
* tegra20_ac97.c - Tegra20 AC97 platform driver
*
* Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
*
* Partly based on code copyright/by:
*
* Copyright (c) 2011,2012 Toradex Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "tegra_asoc_utils.h"
#include "tegra20_ac97.h"
#define DRV_NAME "tegra20-ac97"
static
struct
tegra20_ac97
*
workdata
;
static
void
tegra20_ac97_codec_reset
(
struct
snd_ac97
*
ac97
)
{
u32
readback
;
unsigned
long
timeout
;
/* reset line is not driven by DAC pad group, have to toggle GPIO */
gpio_set_value
(
workdata
->
reset_gpio
,
0
);
udelay
(
2
);
gpio_set_value
(
workdata
->
reset_gpio
,
1
);
udelay
(
2
);
timeout
=
jiffies
+
msecs_to_jiffies
(
100
);
do
{
regmap_read
(
workdata
->
regmap
,
TEGRA20_AC97_STATUS1
,
&
readback
);
if
(
readback
&
TEGRA20_AC97_STATUS1_CODEC1_RDY
)
break
;
usleep_range
(
1000
,
2000
);
}
while
(
!
time_after
(
jiffies
,
timeout
));
}
static
void
tegra20_ac97_codec_warm_reset
(
struct
snd_ac97
*
ac97
)
{
u32
readback
;
unsigned
long
timeout
;
/*
* although sync line is driven by the DAC pad group warm reset using
* the controller cmd is not working, have to toggle sync line
* manually.
*/
gpio_request
(
workdata
->
sync_gpio
,
"codec-sync"
);
gpio_direction_output
(
workdata
->
sync_gpio
,
1
);
udelay
(
2
);
gpio_set_value
(
workdata
->
sync_gpio
,
0
);
udelay
(
2
);
gpio_free
(
workdata
->
sync_gpio
);
timeout
=
jiffies
+
msecs_to_jiffies
(
100
);
do
{
regmap_read
(
workdata
->
regmap
,
TEGRA20_AC97_STATUS1
,
&
readback
);
if
(
readback
&
TEGRA20_AC97_STATUS1_CODEC1_RDY
)
break
;
usleep_range
(
1000
,
2000
);
}
while
(
!
time_after
(
jiffies
,
timeout
));
}
static
unsigned
short
tegra20_ac97_codec_read
(
struct
snd_ac97
*
ac97_snd
,
unsigned
short
reg
)
{
u32
readback
;
unsigned
long
timeout
;
regmap_write
(
workdata
->
regmap
,
TEGRA20_AC97_CMD
,
(((
reg
|
0x80
)
<<
TEGRA20_AC97_CMD_CMD_ADDR_SHIFT
)
&
TEGRA20_AC97_CMD_CMD_ADDR_MASK
)
|
TEGRA20_AC97_CMD_BUSY
);
timeout
=
jiffies
+
msecs_to_jiffies
(
100
);
do
{
regmap_read
(
workdata
->
regmap
,
TEGRA20_AC97_STATUS1
,
&
readback
);
if
(
readback
&
TEGRA20_AC97_STATUS1_STA_VALID1
)
break
;
usleep_range
(
1000
,
2000
);
}
while
(
!
time_after
(
jiffies
,
timeout
));
return
((
readback
&
TEGRA20_AC97_STATUS1_STA_DATA1_MASK
)
>>
TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT
);
}
static
void
tegra20_ac97_codec_write
(
struct
snd_ac97
*
ac97_snd
,
unsigned
short
reg
,
unsigned
short
val
)
{
u32
readback
;
unsigned
long
timeout
;
regmap_write
(
workdata
->
regmap
,
TEGRA20_AC97_CMD
,
((
reg
<<
TEGRA20_AC97_CMD_CMD_ADDR_SHIFT
)
&
TEGRA20_AC97_CMD_CMD_ADDR_MASK
)
|
((
val
<<
TEGRA20_AC97_CMD_CMD_DATA_SHIFT
)
&
TEGRA20_AC97_CMD_CMD_DATA_MASK
)
|
TEGRA20_AC97_CMD_BUSY
);
timeout
=
jiffies
+
msecs_to_jiffies
(
100
);
do
{
regmap_read
(
workdata
->
regmap
,
TEGRA20_AC97_CMD
,
&
readback
);
if
(
!
(
readback
&
TEGRA20_AC97_CMD_BUSY
))
break
;
usleep_range
(
1000
,
2000
);
}
while
(
!
time_after
(
jiffies
,
timeout
));
}
struct
snd_ac97_bus_ops
soc_ac97_ops
=
{
.
read
=
tegra20_ac97_codec_read
,
.
write
=
tegra20_ac97_codec_write
,
.
reset
=
tegra20_ac97_codec_reset
,
.
warm_reset
=
tegra20_ac97_codec_warm_reset
,
};
EXPORT_SYMBOL_GPL
(
soc_ac97_ops
);
static
inline
void
tegra20_ac97_start_playback
(
struct
tegra20_ac97
*
ac97
)
{
regmap_update_bits
(
ac97
->
regmap
,
TEGRA20_AC97_FIFO1_SCR
,
TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
,
TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
);
regmap_update_bits
(
ac97
->
regmap
,
TEGRA20_AC97_CTRL
,
TEGRA20_AC97_CTRL_PCM_DAC_EN
|
TEGRA20_AC97_CTRL_STM_EN
,
TEGRA20_AC97_CTRL_PCM_DAC_EN
|
TEGRA20_AC97_CTRL_STM_EN
);
}
static
inline
void
tegra20_ac97_stop_playback
(
struct
tegra20_ac97
*
ac97
)
{
regmap_update_bits
(
ac97
->
regmap
,
TEGRA20_AC97_FIFO1_SCR
,
TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
,
0
);
regmap_update_bits
(
ac97
->
regmap
,
TEGRA20_AC97_CTRL
,
TEGRA20_AC97_CTRL_PCM_DAC_EN
,
0
);
}
static
inline
void
tegra20_ac97_start_capture
(
struct
tegra20_ac97
*
ac97
)
{
regmap_update_bits
(
ac97
->
regmap
,
TEGRA20_AC97_FIFO1_SCR
,
TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
,
TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
);
}
static
inline
void
tegra20_ac97_stop_capture
(
struct
tegra20_ac97
*
ac97
)
{
regmap_update_bits
(
ac97
->
regmap
,
TEGRA20_AC97_FIFO1_SCR
,
TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
,
0
);
}
static
int
tegra20_ac97_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
,
struct
snd_soc_dai
*
dai
)
{
struct
tegra20_ac97
*
ac97
=
snd_soc_dai_get_drvdata
(
dai
);
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
case
SNDRV_PCM_TRIGGER_RESUME
:
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
tegra20_ac97_start_playback
(
ac97
);
else
tegra20_ac97_start_capture
(
ac97
);
break
;
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
tegra20_ac97_stop_playback
(
ac97
);
else
tegra20_ac97_stop_capture
(
ac97
);
break
;
default:
return
-
EINVAL
;
}
return
0
;
}
static
const
struct
snd_soc_dai_ops
tegra20_ac97_dai_ops
=
{
.
trigger
=
tegra20_ac97_trigger
,
};
static
int
tegra20_ac97_probe
(
struct
snd_soc_dai
*
dai
)
{
struct
tegra20_ac97
*
ac97
=
snd_soc_dai_get_drvdata
(
dai
);
dai
->
capture_dma_data
=
&
ac97
->
capture_dma_data
;
dai
->
playback_dma_data
=
&
ac97
->
playback_dma_data
;
return
0
;
}
static
struct
snd_soc_dai_driver
tegra20_ac97_dai
=
{
.
name
=
"tegra-ac97-pcm"
,
.
ac97_control
=
1
,
.
probe
=
tegra20_ac97_probe
,
.
playback
=
{
.
stream_name
=
"PCM Playback"
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
SNDRV_PCM_RATE_8000_48000
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
},
.
capture
=
{
.
stream_name
=
"PCM Capture"
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
SNDRV_PCM_RATE_8000_48000
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
},
.
ops
=
&
tegra20_ac97_dai_ops
,
};
static
bool
tegra20_ac97_wr_rd_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
TEGRA20_AC97_CTRL
:
case
TEGRA20_AC97_CMD
:
case
TEGRA20_AC97_STATUS1
:
case
TEGRA20_AC97_FIFO1_SCR
:
case
TEGRA20_AC97_FIFO_TX1
:
case
TEGRA20_AC97_FIFO_RX1
:
return
true
;
default:
break
;
}
return
false
;
}
static
bool
tegra20_ac97_volatile_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
TEGRA20_AC97_STATUS1
:
case
TEGRA20_AC97_FIFO1_SCR
:
case
TEGRA20_AC97_FIFO_TX1
:
case
TEGRA20_AC97_FIFO_RX1
:
return
true
;
default:
break
;
}
return
false
;
}
static
bool
tegra20_ac97_precious_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
TEGRA20_AC97_FIFO_TX1
:
case
TEGRA20_AC97_FIFO_RX1
:
return
true
;
default:
break
;
}
return
false
;
}
static
const
struct
regmap_config
tegra20_ac97_regmap_config
=
{
.
reg_bits
=
32
,
.
reg_stride
=
4
,
.
val_bits
=
32
,
.
max_register
=
TEGRA20_AC97_FIFO_RX1
,
.
writeable_reg
=
tegra20_ac97_wr_rd_reg
,
.
readable_reg
=
tegra20_ac97_wr_rd_reg
,
.
volatile_reg
=
tegra20_ac97_volatile_reg
,
.
precious_reg
=
tegra20_ac97_precious_reg
,
.
cache_type
=
REGCACHE_RBTREE
,
};
static
int
tegra20_ac97_platform_probe
(
struct
platform_device
*
pdev
)
{
struct
tegra20_ac97
*
ac97
;
struct
resource
*
mem
,
*
memregion
;
u32
of_dma
[
2
];
void
__iomem
*
regs
;
int
ret
=
0
;
ac97
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
struct
tegra20_ac97
),
GFP_KERNEL
);
if
(
!
ac97
)
{
dev_err
(
&
pdev
->
dev
,
"Can't allocate tegra20_ac97
\n
"
);
ret
=
-
ENOMEM
;
goto
err
;
}
dev_set_drvdata
(
&
pdev
->
dev
,
ac97
);
ac97
->
clk_ac97
=
clk_get
(
&
pdev
->
dev
,
NULL
);
if
(
IS_ERR
(
ac97
->
clk_ac97
))
{
dev_err
(
&
pdev
->
dev
,
"Can't retrieve ac97 clock
\n
"
);
ret
=
PTR_ERR
(
ac97
->
clk_ac97
);
goto
err
;
}
mem
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
mem
)
{
dev_err
(
&
pdev
->
dev
,
"No memory resource
\n
"
);
ret
=
-
ENODEV
;
goto
err_clk_put
;
}
memregion
=
devm_request_mem_region
(
&
pdev
->
dev
,
mem
->
start
,
resource_size
(
mem
),
DRV_NAME
);
if
(
!
memregion
)
{
dev_err
(
&
pdev
->
dev
,
"Memory region already claimed
\n
"
);
ret
=
-
EBUSY
;
goto
err_clk_put
;
}
regs
=
devm_ioremap
(
&
pdev
->
dev
,
mem
->
start
,
resource_size
(
mem
));
if
(
!
regs
)
{
dev_err
(
&
pdev
->
dev
,
"ioremap failed
\n
"
);
ret
=
-
ENOMEM
;
goto
err_clk_put
;
}
ac97
->
regmap
=
devm_regmap_init_mmio
(
&
pdev
->
dev
,
regs
,
&
tegra20_ac97_regmap_config
);
if
(
IS_ERR
(
ac97
->
regmap
))
{
dev_err
(
&
pdev
->
dev
,
"regmap init failed
\n
"
);
ret
=
PTR_ERR
(
ac97
->
regmap
);
goto
err_clk_put
;
}
if
(
of_property_read_u32_array
(
pdev
->
dev
.
of_node
,
"nvidia,dma-request-selector"
,
of_dma
,
2
)
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"No DMA resource
\n
"
);
ret
=
-
ENODEV
;
goto
err_clk_put
;
}
ac97
->
reset_gpio
=
of_get_named_gpio
(
pdev
->
dev
.
of_node
,
"nvidia,codec-reset-gpio"
,
0
);
if
(
gpio_is_valid
(
ac97
->
reset_gpio
))
{
ret
=
devm_gpio_request_one
(
&
pdev
->
dev
,
ac97
->
reset_gpio
,
GPIOF_OUT_INIT_HIGH
,
"codec-reset"
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"could not get codec-reset GPIO
\n
"
);
goto
err_clk_put
;
}
}
else
{
dev_err
(
&
pdev
->
dev
,
"no codec-reset GPIO supplied
\n
"
);
goto
err_clk_put
;
}
ac97
->
sync_gpio
=
of_get_named_gpio
(
pdev
->
dev
.
of_node
,
"nvidia,codec-sync-gpio"
,
0
);
if
(
!
gpio_is_valid
(
ac97
->
sync_gpio
))
{
dev_err
(
&
pdev
->
dev
,
"no codec-sync GPIO supplied
\n
"
);
goto
err_clk_put
;
}
ac97
->
capture_dma_data
.
addr
=
mem
->
start
+
TEGRA20_AC97_FIFO_RX1
;
ac97
->
capture_dma_data
.
wrap
=
4
;
ac97
->
capture_dma_data
.
width
=
32
;
ac97
->
capture_dma_data
.
req_sel
=
of_dma
[
1
];
ac97
->
playback_dma_data
.
addr
=
mem
->
start
+
TEGRA20_AC97_FIFO_TX1
;
ac97
->
playback_dma_data
.
wrap
=
4
;
ac97
->
playback_dma_data
.
width
=
32
;
ac97
->
playback_dma_data
.
req_sel
=
of_dma
[
1
];
ret
=
snd_soc_register_dais
(
&
pdev
->
dev
,
&
tegra20_ac97_dai
,
1
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Could not register DAI: %d
\n
"
,
ret
);
ret
=
-
ENOMEM
;
goto
err_clk_put
;
}
ret
=
tegra_pcm_platform_register
(
&
pdev
->
dev
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Could not register PCM: %d
\n
"
,
ret
);
goto
err_unregister_dai
;
}
ret
=
tegra_asoc_utils_init
(
&
ac97
->
util_data
,
&
pdev
->
dev
);
if
(
ret
)
goto
err_unregister_pcm
;
ret
=
tegra_asoc_utils_set_ac97_rate
(
&
ac97
->
util_data
);
if
(
ret
)
goto
err_asoc_utils_fini
;
ret
=
clk_prepare_enable
(
ac97
->
clk_ac97
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"clk_enable failed: %d
\n
"
,
ret
);
goto
err_asoc_utils_fini
;
}
/* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
workdata
=
ac97
;
return
0
;
err_asoc_utils_fini:
tegra_asoc_utils_fini
(
&
ac97
->
util_data
);
err_unregister_pcm:
tegra_pcm_platform_unregister
(
&
pdev
->
dev
);
err_unregister_dai:
snd_soc_unregister_dai
(
&
pdev
->
dev
);
err_clk_put:
clk_put
(
ac97
->
clk_ac97
);
err:
return
ret
;
}
static
int
tegra20_ac97_platform_remove
(
struct
platform_device
*
pdev
)
{
struct
tegra20_ac97
*
ac97
=
dev_get_drvdata
(
&
pdev
->
dev
);
tegra_pcm_platform_unregister
(
&
pdev
->
dev
);
snd_soc_unregister_dai
(
&
pdev
->
dev
);
tegra_asoc_utils_fini
(
&
ac97
->
util_data
);
clk_disable_unprepare
(
ac97
->
clk_ac97
);
clk_put
(
ac97
->
clk_ac97
);
return
0
;
}
static
const
struct
of_device_id
tegra20_ac97_of_match
[]
=
{
{
.
compatible
=
"nvidia,tegra20-ac97"
,
},
{},
};
static
struct
platform_driver
tegra20_ac97_driver
=
{
.
driver
=
{
.
name
=
DRV_NAME
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
tegra20_ac97_of_match
,
},
.
probe
=
tegra20_ac97_platform_probe
,
.
remove
=
tegra20_ac97_platform_remove
,
};
module_platform_driver
(
tegra20_ac97_driver
);
MODULE_AUTHOR
(
"Lucas Stach"
);
MODULE_DESCRIPTION
(
"Tegra20 AC97 ASoC driver"
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_ALIAS
(
"platform:"
DRV_NAME
);
MODULE_DEVICE_TABLE
(
of
,
tegra20_ac97_of_match
);
sound/soc/tegra/tegra20_ac97.h
0 → 100644
View file @
56b3f31f
/*
* tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver
*
* Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
*
* Partly based on code copyright/by:
*
* Copyright (c) 2011,2012 Toradex Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*/
#ifndef __TEGRA20_AC97_H__
#define __TEGRA20_AC97_H__
#include "tegra_pcm.h"
#define TEGRA20_AC97_CTRL 0x00
#define TEGRA20_AC97_CMD 0x04
#define TEGRA20_AC97_STATUS1 0x08
/* ... */
#define TEGRA20_AC97_FIFO1_SCR 0x1c
/* ... */
#define TEGRA20_AC97_FIFO_TX1 0x40
#define TEGRA20_AC97_FIFO_RX1 0x80
/* TEGRA20_AC97_CTRL */
#define TEGRA20_AC97_CTRL_STM2_EN (1 << 16)
#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN (1 << 11)
#define TEGRA20_AC97_CTRL_IO_CNTRL_EN (1 << 10)
#define TEGRA20_AC97_CTRL_HSET_DAC_EN (1 << 9)
#define TEGRA20_AC97_CTRL_LINE2_DAC_EN (1 << 8)
#define TEGRA20_AC97_CTRL_PCM_LFE_EN (1 << 7)
#define TEGRA20_AC97_CTRL_PCM_SUR_EN (1 << 6)
#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN (1 << 5)
#define TEGRA20_AC97_CTRL_LINE1_DAC_EN (1 << 4)
#define TEGRA20_AC97_CTRL_PCM_DAC_EN (1 << 3)
#define TEGRA20_AC97_CTRL_COLD_RESET (1 << 2)
#define TEGRA20_AC97_CTRL_WARM_RESET (1 << 1)
#define TEGRA20_AC97_CTRL_STM_EN (1 << 0)
/* TEGRA20_AC97_CMD */
#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT 24
#define TEGRA20_AC97_CMD_CMD_ADDR_MASK (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT)
#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT 8
#define TEGRA20_AC97_CMD_CMD_DATA_MASK (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT)
#define TEGRA20_AC97_CMD_CMD_ID_SHIFT 2
#define TEGRA20_AC97_CMD_CMD_ID_MASK (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT)
#define TEGRA20_AC97_CMD_BUSY (1 << 0)
/* TEGRA20_AC97_STATUS1 */
#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT 24
#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT)
#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT 8
#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT)
#define TEGRA20_AC97_STATUS1_STA_VALID1 (1 << 2)
#define TEGRA20_AC97_STATUS1_STANDBY1 (1 << 1)
#define TEGRA20_AC97_STATUS1_CODEC1_RDY (1 << 0)
/* TEGRA20_AC97_FIFO1_SCR */
#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT 27
#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT)
#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT 22
#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT)
#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA (1 << 19)
#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA (1 << 18)
#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT (1 << 17)
#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT (1 << 16)
#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN (1 << 15)
#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN (1 << 14)
#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN (1 << 13)
#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN (1 << 12)
#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN (1 << 11)
#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN (1 << 10)
#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN (1 << 9)
#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN (1 << 8)
struct
tegra20_ac97
{
struct
clk
*
clk_ac97
;
struct
tegra_pcm_dma_params
capture_dma_data
;
struct
tegra_pcm_dma_params
playback_dma_data
;
struct
regmap
*
regmap
;
int
reset_gpio
;
int
sync_gpio
;
struct
tegra_asoc_utils_data
util_data
;
};
#endif
/* __TEGRA20_AC97_H__ */
sound/soc/tegra/tegra20_das.c
View file @
56b3f31f
...
...
@@ -191,6 +191,19 @@ static int tegra20_das_probe(struct platform_device *pdev)
goto
err
;
}
ret
=
tegra20_das_connect_dap_to_dac
(
TEGRA20_DAS_DAP_ID_3
,
TEGRA20_DAS_DAP_SEL_DAC3
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Can't set up DAS DAP connection
\n
"
);
goto
err
;
}
ret
=
tegra20_das_connect_dac_to_dap
(
TEGRA20_DAS_DAC_ID_3
,
TEGRA20_DAS_DAC_SEL_DAP3
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Can't set up DAS DAC connection
\n
"
);
goto
err
;
}
platform_set_drvdata
(
pdev
,
das
);
return
0
;
...
...
sound/soc/tegra/tegra30_ahub.c
View file @
56b3f31f
...
...
@@ -580,7 +580,7 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
clk_put
(
ahub
->
clk_apbif
);
err_clk_put_d_audio:
clk_put
(
ahub
->
clk_d_audio
);
ahub
=
0
;
ahub
=
NULL
;
err:
return
ret
;
}
...
...
@@ -597,7 +597,7 @@ static int tegra30_ahub_remove(struct platform_device *pdev)
clk_put
(
ahub
->
clk_apbif
);
clk_put
(
ahub
->
clk_d_audio
);
ahub
=
0
;
ahub
=
NULL
;
return
0
;
}
...
...
sound/soc/tegra/tegra30_i2s.c
View file @
56b3f31f
...
...
@@ -71,7 +71,7 @@ static int tegra30_i2s_runtime_resume(struct device *dev)
return
0
;
}
int
tegra30_i2s_startup
(
struct
snd_pcm_substream
*
substream
,
static
int
tegra30_i2s_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
tegra30_i2s
*
i2s
=
snd_soc_dai_get_drvdata
(
dai
);
...
...
@@ -98,7 +98,7 @@ int tegra30_i2s_startup(struct snd_pcm_substream *substream,
return
ret
;
}
void
tegra30_i2s_shutdown
(
struct
snd_pcm_substream
*
substream
,
static
void
tegra30_i2s_shutdown
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
tegra30_i2s
*
i2s
=
snd_soc_dai_get_drvdata
(
dai
);
...
...
sound/soc/tegra/tegra_asoc_utils.c
View file @
56b3f31f
...
...
@@ -112,6 +112,59 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
}
EXPORT_SYMBOL_GPL
(
tegra_asoc_utils_set_rate
);
int
tegra_asoc_utils_set_ac97_rate
(
struct
tegra_asoc_utils_data
*
data
)
{
const
int
pll_rate
=
73728000
;
const
int
ac97_rate
=
24576000
;
int
err
;
clk_disable_unprepare
(
data
->
clk_cdev1
);
clk_disable_unprepare
(
data
->
clk_pll_a_out0
);
clk_disable_unprepare
(
data
->
clk_pll_a
);
/*
* AC97 rate is fixed at 24.576MHz and is used for both the host
* controller and the external codec
*/
err
=
clk_set_rate
(
data
->
clk_pll_a
,
pll_rate
);
if
(
err
)
{
dev_err
(
data
->
dev
,
"Can't set pll_a rate: %d
\n
"
,
err
);
return
err
;
}
err
=
clk_set_rate
(
data
->
clk_pll_a_out0
,
ac97_rate
);
if
(
err
)
{
dev_err
(
data
->
dev
,
"Can't set pll_a_out0 rate: %d
\n
"
,
err
);
return
err
;
}
/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
err
=
clk_prepare_enable
(
data
->
clk_pll_a
);
if
(
err
)
{
dev_err
(
data
->
dev
,
"Can't enable pll_a: %d
\n
"
,
err
);
return
err
;
}
err
=
clk_prepare_enable
(
data
->
clk_pll_a_out0
);
if
(
err
)
{
dev_err
(
data
->
dev
,
"Can't enable pll_a_out0: %d
\n
"
,
err
);
return
err
;
}
err
=
clk_prepare_enable
(
data
->
clk_cdev1
);
if
(
err
)
{
dev_err
(
data
->
dev
,
"Can't enable cdev1: %d
\n
"
,
err
);
return
err
;
}
data
->
set_baseclock
=
pll_rate
;
data
->
set_mclk
=
ac97_rate
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
tegra_asoc_utils_set_ac97_rate
);
int
tegra_asoc_utils_init
(
struct
tegra_asoc_utils_data
*
data
,
struct
device
*
dev
)
{
...
...
sound/soc/tegra/tegra_asoc_utils.h
View file @
56b3f31f
...
...
@@ -43,6 +43,7 @@ struct tegra_asoc_utils_data {
int
tegra_asoc_utils_set_rate
(
struct
tegra_asoc_utils_data
*
data
,
int
srate
,
int
mclk
);
int
tegra_asoc_utils_set_ac97_rate
(
struct
tegra_asoc_utils_data
*
data
);
int
tegra_asoc_utils_init
(
struct
tegra_asoc_utils_data
*
data
,
struct
device
*
dev
);
void
tegra_asoc_utils_fini
(
struct
tegra_asoc_utils_data
*
data
);
...
...
sound/soc/tegra/tegra_wm9712.c
0 → 100644
View file @
56b3f31f
/*
* tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec.
*
* Copyright 2012 Lucas Stach <dev@lynxeye.de>
*
* Partly based on code copyright/by:
* Copyright 2011,2012 Toradex Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#define DRV_NAME "tegra-snd-wm9712"
struct
tegra_wm9712
{
struct
platform_device
*
codec
;
};
static
const
struct
snd_soc_dapm_widget
tegra_wm9712_dapm_widgets
[]
=
{
SND_SOC_DAPM_HP
(
"Headphone"
,
NULL
),
SND_SOC_DAPM_LINE
(
"LineIn"
,
NULL
),
SND_SOC_DAPM_MIC
(
"Mic"
,
NULL
),
};
static
int
tegra_wm9712_init
(
struct
snd_soc_pcm_runtime
*
rtd
)
{
struct
snd_soc_dai
*
codec_dai
=
rtd
->
codec_dai
;
struct
snd_soc_codec
*
codec
=
codec_dai
->
codec
;
struct
snd_soc_dapm_context
*
dapm
=
&
codec
->
dapm
;
snd_soc_dapm_force_enable_pin
(
dapm
,
"Mic Bias"
);
return
snd_soc_dapm_sync
(
dapm
);
}
static
struct
snd_soc_dai_link
tegra_wm9712_dai
=
{
.
name
=
"AC97 HiFi"
,
.
stream_name
=
"AC97 HiFi"
,
.
cpu_dai_name
=
"tegra-ac97-pcm"
,
.
codec_dai_name
=
"wm9712-hifi"
,
.
codec_name
=
"wm9712-codec"
,
.
init
=
tegra_wm9712_init
,
};
static
struct
snd_soc_card
snd_soc_tegra_wm9712
=
{
.
name
=
"tegra-wm9712"
,
.
owner
=
THIS_MODULE
,
.
dai_link
=
&
tegra_wm9712_dai
,
.
num_links
=
1
,
.
dapm_widgets
=
tegra_wm9712_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
tegra_wm9712_dapm_widgets
),
.
fully_routed
=
true
,
};
static
int
tegra_wm9712_driver_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
struct
snd_soc_card
*
card
=
&
snd_soc_tegra_wm9712
;
struct
tegra_wm9712
*
machine
;
int
ret
;
if
(
!
pdev
->
dev
.
of_node
)
{
dev_err
(
&
pdev
->
dev
,
"No platform data supplied
\n
"
);
return
-
EINVAL
;
}
machine
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
struct
tegra_wm9712
),
GFP_KERNEL
);
if
(
!
machine
)
{
dev_err
(
&
pdev
->
dev
,
"Can't allocate tegra_wm9712 struct
\n
"
);
return
-
ENOMEM
;
}
card
->
dev
=
&
pdev
->
dev
;
platform_set_drvdata
(
pdev
,
card
);
snd_soc_card_set_drvdata
(
card
,
machine
);
machine
->
codec
=
platform_device_alloc
(
"wm9712-codec"
,
-
1
);
if
(
!
machine
->
codec
)
{
dev_err
(
&
pdev
->
dev
,
"Can't allocate wm9712 platform device
\n
"
);
return
-
ENOMEM
;
}
ret
=
platform_device_add
(
machine
->
codec
);
if
(
ret
)
goto
codec_put
;
ret
=
snd_soc_of_parse_card_name
(
card
,
"nvidia,model"
);
if
(
ret
)
goto
codec_unregister
;
ret
=
snd_soc_of_parse_audio_routing
(
card
,
"nvidia,audio-routing"
);
if
(
ret
)
goto
codec_unregister
;
tegra_wm9712_dai
.
cpu_of_node
=
of_parse_phandle
(
np
,
"nvidia,ac97-controller"
,
0
);
if
(
!
tegra_wm9712_dai
.
cpu_of_node
)
{
dev_err
(
&
pdev
->
dev
,
"Property 'nvidia,ac97-controller' missing or invalid
\n
"
);
ret
=
-
EINVAL
;
goto
codec_unregister
;
}
tegra_wm9712_dai
.
platform_of_node
=
tegra_wm9712_dai
.
cpu_of_node
;
ret
=
snd_soc_register_card
(
card
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"snd_soc_register_card failed (%d)
\n
"
,
ret
);
goto
codec_unregister
;
}
return
0
;
codec_unregister:
platform_device_del
(
machine
->
codec
);
codec_put:
platform_device_put
(
machine
->
codec
);
return
ret
;
}
static
int
tegra_wm9712_driver_remove
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_card
*
card
=
platform_get_drvdata
(
pdev
);
struct
tegra_wm9712
*
machine
=
snd_soc_card_get_drvdata
(
card
);
snd_soc_unregister_card
(
card
);
platform_device_unregister
(
machine
->
codec
);
return
0
;
}
static
const
struct
of_device_id
tegra_wm9712_of_match
[]
=
{
{
.
compatible
=
"nvidia,tegra-audio-wm9712"
,
},
{},
};
static
struct
platform_driver
tegra_wm9712_driver
=
{
.
driver
=
{
.
name
=
DRV_NAME
,
.
owner
=
THIS_MODULE
,
.
pm
=
&
snd_soc_pm_ops
,
.
of_match_table
=
tegra_wm9712_of_match
,
},
.
probe
=
tegra_wm9712_driver_probe
,
.
remove
=
tegra_wm9712_driver_remove
,
};
module_platform_driver
(
tegra_wm9712_driver
);
MODULE_AUTHOR
(
"Lucas Stach"
);
MODULE_DESCRIPTION
(
"Tegra+WM9712 machine ASoC driver"
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_ALIAS
(
"platform:"
DRV_NAME
);
MODULE_DEVICE_TABLE
(
of
,
tegra_wm9712_of_match
);
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