Merge remote-tracking branches 'asoc/topic/rt5665', 'asoc/topic/rt5677', 'asoc/topic/samsung', 'asoc/topic/simple' and 'asoc/topic/sunxi' into asoc-next
This commit is contained in:
Коммит
166729f37d
|
@ -7,6 +7,7 @@ Required properties:
|
||||||
|
|
||||||
- compatible: should be one of the followings
|
- compatible: should be one of the followings
|
||||||
- "allwinner,sun4i-a10-i2s"
|
- "allwinner,sun4i-a10-i2s"
|
||||||
|
- "allwinner,sun6i-a31-i2s"
|
||||||
- reg: physical base address of the controller and length of memory mapped
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
region.
|
region.
|
||||||
- interrupts: should contain the I2S interrupt.
|
- interrupts: should contain the I2S interrupt.
|
||||||
|
@ -19,6 +20,10 @@ Required properties:
|
||||||
- "mod" : module clock for the I2S controller
|
- "mod" : module clock for the I2S controller
|
||||||
- #sound-dai-cells : Must be equal to 0
|
- #sound-dai-cells : Must be equal to 0
|
||||||
|
|
||||||
|
Required properties for the following compatibles:
|
||||||
|
- "allwinner,sun6i-a31-i2s"
|
||||||
|
- resets: phandle to the reset line for this codec
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
i2s0: i2s@01c22400 {
|
i2s0: i2s@01c22400 {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
Allwinner SUN8I audio codec
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
On Sun8i-A33 SoCs, the audio is separated in different parts:
|
||||||
|
- A DAI driver. It uses the "sun4i-i2s" driver which is
|
||||||
|
documented here:
|
||||||
|
Documentation/devicetree/bindings/sound/sun4i-i2s.txt
|
||||||
|
- An analog part of the codec which is handled as PRCM registers.
|
||||||
|
See Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt
|
||||||
|
- An digital part of the codec which is documented in this current
|
||||||
|
binding documentation.
|
||||||
|
- And finally, an audio card which links all the above components.
|
||||||
|
The simple-audio card will be used.
|
||||||
|
See Documentation/devicetree/bindings/sound/simple-card.txt
|
||||||
|
|
||||||
|
This bindings documentation exposes Sun8i codec (digital part).
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: must be "allwinner,sun8i-a33-codec"
|
||||||
|
- reg: must contain the registers location and length
|
||||||
|
- interrupts: must contain the codec interrupt
|
||||||
|
- clocks: a list of phandle + clock-specifer pairs, one for each entry
|
||||||
|
in clock-names.
|
||||||
|
- clock-names: should contain followings:
|
||||||
|
- "bus": the parent APB clock for this controller
|
||||||
|
- "mod": the parent module clock
|
||||||
|
|
||||||
|
Here is an example to add a sound card and the codec binding on sun8i SoCs that
|
||||||
|
are similar to A33 using simple-card:
|
||||||
|
|
||||||
|
sound {
|
||||||
|
compatible = "simple-audio-card";
|
||||||
|
simple-audio-card,name = "sun8i-a33-audio";
|
||||||
|
simple-audio-card,format = "i2s";
|
||||||
|
simple-audio-card,frame-master = <&link_codec>;
|
||||||
|
simple-audio-card,bitclock-master = <&link_codec>;
|
||||||
|
simple-audio-card,mclk-fs = <512>;
|
||||||
|
simple-audio-card,aux-devs = <&codec_analog>;
|
||||||
|
simple-audio-card,routing =
|
||||||
|
"Left DAC", "Digital Left DAC",
|
||||||
|
"Right DAC", "Digital Right DAC";
|
||||||
|
|
||||||
|
simple-audio-card,cpu {
|
||||||
|
sound-dai = <&dai>;
|
||||||
|
};
|
||||||
|
|
||||||
|
link_codec: simple-audio-card,codec {
|
||||||
|
sound-dai = <&codec>;
|
||||||
|
};
|
||||||
|
|
||||||
|
soc@01c00000 {
|
||||||
|
[...]
|
||||||
|
|
||||||
|
audio-codec@1c22e00 {
|
||||||
|
#sound-dai-cells = <0>;
|
||||||
|
compatible = "allwinner,sun8i-a33-codec";
|
||||||
|
reg = <0x01c22e00 0x400>;
|
||||||
|
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&ccu CLK_BUS_CODEC>, <&ccu CLK_AC_DIG>;
|
||||||
|
clock-names = "bus", "mod";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ Required properties:
|
||||||
- compatible : should be one of the following:
|
- compatible : should be one of the following:
|
||||||
- "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
|
- "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
|
||||||
- "allwinner,sun6i-a31-spdif": for the Allwinner A31 SoC
|
- "allwinner,sun6i-a31-spdif": for the Allwinner A31 SoC
|
||||||
|
- "allwinner,sun8i-h3-spdif": for the Allwinner H3 SoC
|
||||||
|
|
||||||
- reg : Offset and length of the register set for the device.
|
- reg : Offset and length of the register set for the device.
|
||||||
|
|
||||||
|
|
|
@ -106,9 +106,7 @@ static struct s3c_audio_pdata i2sv4_pdata = {
|
||||||
.dma_playback = DMACH_HSI_I2SV40_TX,
|
.dma_playback = DMACH_HSI_I2SV40_TX,
|
||||||
.dma_capture = DMACH_HSI_I2SV40_RX,
|
.dma_capture = DMACH_HSI_I2SV40_RX,
|
||||||
.type = {
|
.type = {
|
||||||
.i2s = {
|
.quirks = QUIRK_PRI_6CHAN,
|
||||||
.quirks = QUIRK_PRI_6CHAN,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
extern void s3c64xx_ac97_setup_gpio(int);
|
extern void s3c64xx_ac97_setup_gpio(int);
|
||||||
|
|
||||||
struct samsung_i2s {
|
struct samsung_i2s_type {
|
||||||
/* If the Primary DAI has 5.1 Channels */
|
/* If the Primary DAI has 5.1 Channels */
|
||||||
#define QUIRK_PRI_6CHAN (1 << 0)
|
#define QUIRK_PRI_6CHAN (1 << 0)
|
||||||
/* If the I2S block has a Stereo Overlay Channel */
|
/* If the I2S block has a Stereo Overlay Channel */
|
||||||
|
@ -47,7 +47,5 @@ struct s3c_audio_pdata {
|
||||||
void *dma_capture;
|
void *dma_capture;
|
||||||
void *dma_play_sec;
|
void *dma_play_sec;
|
||||||
void *dma_capture_mic;
|
void *dma_capture_mic;
|
||||||
union {
|
struct samsung_i2s_type type;
|
||||||
struct samsung_i2s i2s;
|
|
||||||
} type;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,11 +34,12 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
|
||||||
int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
||||||
char *prefix);
|
char *prefix);
|
||||||
|
|
||||||
#define asoc_simple_card_parse_clk_cpu(node, dai_link, simple_dai) \
|
#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||||
asoc_simple_card_parse_clk(node, dai_link->cpu_of_node, simple_dai)
|
asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai)
|
||||||
#define asoc_simple_card_parse_clk_codec(node, dai_link, simple_dai) \
|
#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||||
asoc_simple_card_parse_clk(node, dai_link->codec_of_node, simple_dai)
|
asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai)
|
||||||
int asoc_simple_card_parse_clk(struct device_node *node,
|
int asoc_simple_card_parse_clk(struct device *dev,
|
||||||
|
struct device_node *node,
|
||||||
struct device_node *dai_of_node,
|
struct device_node *dai_of_node,
|
||||||
struct asoc_simple_dai *simple_dai);
|
struct asoc_simple_dai *simple_dai);
|
||||||
|
|
||||||
|
|
|
@ -118,8 +118,8 @@ config SND_SOC_ALL_CODECS
|
||||||
select SND_SOC_RT5651 if I2C
|
select SND_SOC_RT5651 if I2C
|
||||||
select SND_SOC_RT5659 if I2C
|
select SND_SOC_RT5659 if I2C
|
||||||
select SND_SOC_RT5660 if I2C
|
select SND_SOC_RT5660 if I2C
|
||||||
select SND_SOC_RT5665 if I2C
|
|
||||||
select SND_SOC_RT5663 if I2C
|
select SND_SOC_RT5663 if I2C
|
||||||
|
select SND_SOC_RT5665 if I2C
|
||||||
select SND_SOC_RT5670 if I2C
|
select SND_SOC_RT5670 if I2C
|
||||||
select SND_SOC_RT5677 if I2C && SPI_MASTER
|
select SND_SOC_RT5677 if I2C && SPI_MASTER
|
||||||
select SND_SOC_SGTL5000 if I2C
|
select SND_SOC_SGTL5000 if I2C
|
||||||
|
@ -671,8 +671,8 @@ config SND_SOC_RL6231
|
||||||
default y if SND_SOC_RT5651=y
|
default y if SND_SOC_RT5651=y
|
||||||
default y if SND_SOC_RT5659=y
|
default y if SND_SOC_RT5659=y
|
||||||
default y if SND_SOC_RT5660=y
|
default y if SND_SOC_RT5660=y
|
||||||
default y if SND_SOC_RT5665=y
|
|
||||||
default y if SND_SOC_RT5663=y
|
default y if SND_SOC_RT5663=y
|
||||||
|
default y if SND_SOC_RT5665=y
|
||||||
default y if SND_SOC_RT5670=y
|
default y if SND_SOC_RT5670=y
|
||||||
default y if SND_SOC_RT5677=y
|
default y if SND_SOC_RT5677=y
|
||||||
default m if SND_SOC_RT5514=m
|
default m if SND_SOC_RT5514=m
|
||||||
|
@ -682,8 +682,8 @@ config SND_SOC_RL6231
|
||||||
default m if SND_SOC_RT5651=m
|
default m if SND_SOC_RT5651=m
|
||||||
default m if SND_SOC_RT5659=m
|
default m if SND_SOC_RT5659=m
|
||||||
default m if SND_SOC_RT5660=m
|
default m if SND_SOC_RT5660=m
|
||||||
default m if SND_SOC_RT5665=m
|
|
||||||
default m if SND_SOC_RT5663=m
|
default m if SND_SOC_RT5663=m
|
||||||
|
default m if SND_SOC_RT5665=m
|
||||||
default m if SND_SOC_RT5670=m
|
default m if SND_SOC_RT5670=m
|
||||||
default m if SND_SOC_RT5677=m
|
default m if SND_SOC_RT5677=m
|
||||||
|
|
||||||
|
@ -731,10 +731,10 @@ config SND_SOC_RT5659
|
||||||
config SND_SOC_RT5660
|
config SND_SOC_RT5660
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
config SND_SOC_RT5665
|
config SND_SOC_RT5663
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
config SND_SOC_RT5663
|
config SND_SOC_RT5665
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
config SND_SOC_RT5670
|
config SND_SOC_RT5670
|
||||||
|
|
|
@ -119,8 +119,8 @@ snd-soc-rt5645-objs := rt5645.o
|
||||||
snd-soc-rt5651-objs := rt5651.o
|
snd-soc-rt5651-objs := rt5651.o
|
||||||
snd-soc-rt5659-objs := rt5659.o
|
snd-soc-rt5659-objs := rt5659.o
|
||||||
snd-soc-rt5660-objs := rt5660.o
|
snd-soc-rt5660-objs := rt5660.o
|
||||||
snd-soc-rt5665-objs := rt5665.o
|
|
||||||
snd-soc-rt5663-objs := rt5663.o
|
snd-soc-rt5663-objs := rt5663.o
|
||||||
|
snd-soc-rt5665-objs := rt5665.o
|
||||||
snd-soc-rt5670-objs := rt5670.o
|
snd-soc-rt5670-objs := rt5670.o
|
||||||
snd-soc-rt5677-objs := rt5677.o
|
snd-soc-rt5677-objs := rt5677.o
|
||||||
snd-soc-rt5677-spi-objs := rt5677-spi.o
|
snd-soc-rt5677-spi-objs := rt5677-spi.o
|
||||||
|
@ -348,8 +348,8 @@ obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
|
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
|
obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o
|
obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o
|
|
||||||
obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o
|
obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o
|
||||||
|
obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
|
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
|
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
|
||||||
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
|
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
|
|
@ -98,7 +98,8 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
|
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
|
||||||
|
|
||||||
int asoc_simple_card_parse_clk(struct device_node *node,
|
int asoc_simple_card_parse_clk(struct device *dev,
|
||||||
|
struct device_node *node,
|
||||||
struct device_node *dai_of_node,
|
struct device_node *dai_of_node,
|
||||||
struct asoc_simple_dai *simple_dai)
|
struct asoc_simple_dai *simple_dai)
|
||||||
{
|
{
|
||||||
|
@ -111,14 +112,13 @@ int asoc_simple_card_parse_clk(struct device_node *node,
|
||||||
* or "system-clock-frequency = <xxx>"
|
* or "system-clock-frequency = <xxx>"
|
||||||
* or device's module clock.
|
* or device's module clock.
|
||||||
*/
|
*/
|
||||||
clk = of_clk_get(node, 0);
|
clk = devm_get_clk_from_child(dev, node, NULL);
|
||||||
if (!IS_ERR(clk)) {
|
if (!IS_ERR(clk)) {
|
||||||
simple_dai->sysclk = clk_get_rate(clk);
|
simple_dai->sysclk = clk_get_rate(clk);
|
||||||
simple_dai->clk = clk;
|
|
||||||
} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
|
} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
|
||||||
simple_dai->sysclk = val;
|
simple_dai->sysclk = val;
|
||||||
} else {
|
} else {
|
||||||
clk = of_clk_get(dai_of_node, 0);
|
clk = devm_get_clk_from_child(dev, dai_of_node, NULL);
|
||||||
if (!IS_ERR(clk))
|
if (!IS_ERR(clk))
|
||||||
simple_dai->sysclk = clk_get_rate(clk);
|
simple_dai->sysclk = clk_get_rate(clk);
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,11 +278,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto dai_link_of_err;
|
goto dai_link_of_err;
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
|
ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto dai_link_of_err;
|
goto dai_link_of_err;
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
|
ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto dai_link_of_err;
|
goto dai_link_of_err;
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props);
|
ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai_props);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props);
|
ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai_props);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ config SND_SOC_SAMSUNG_RX1950_UDA1380
|
||||||
config SND_SOC_SMARTQ
|
config SND_SOC_SMARTQ
|
||||||
tristate "SoC I2S Audio support for SmartQ board"
|
tristate "SoC I2S Audio support for SmartQ board"
|
||||||
depends on MACH_SMARTQ || COMPILE_TEST
|
depends on MACH_SMARTQ || COMPILE_TEST
|
||||||
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
depends on I2C
|
depends on I2C
|
||||||
select SND_SAMSUNG_I2S
|
select SND_SAMSUNG_I2S
|
||||||
select SND_SOC_WM8750
|
select SND_SOC_WM8750
|
||||||
|
@ -193,6 +194,7 @@ config SND_SOC_ARNDALE_RT5631_ALC5631
|
||||||
config SND_SOC_SAMSUNG_TM2_WM5110
|
config SND_SOC_SAMSUNG_TM2_WM5110
|
||||||
tristate "SoC I2S Audio support for WM5110 on TM2 board"
|
tristate "SoC I2S Audio support for WM5110 on TM2 board"
|
||||||
depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
|
depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
|
||||||
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
select SND_SOC_MAX98504
|
select SND_SOC_MAX98504
|
||||||
select SND_SOC_WM5110
|
select SND_SOC_WM5110
|
||||||
select SND_SAMSUNG_I2S
|
select SND_SAMSUNG_I2S
|
||||||
|
|
|
@ -34,11 +34,6 @@
|
||||||
|
|
||||||
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
|
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
|
||||||
|
|
||||||
enum samsung_dai_type {
|
|
||||||
TYPE_PRI,
|
|
||||||
TYPE_SEC,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct samsung_i2s_variant_regs {
|
struct samsung_i2s_variant_regs {
|
||||||
unsigned int bfs_off;
|
unsigned int bfs_off;
|
||||||
unsigned int rfs_off;
|
unsigned int rfs_off;
|
||||||
|
@ -54,7 +49,6 @@ struct samsung_i2s_variant_regs {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct samsung_i2s_dai_data {
|
struct samsung_i2s_dai_data {
|
||||||
int dai_type;
|
|
||||||
u32 quirks;
|
u32 quirks;
|
||||||
const struct samsung_i2s_variant_regs *i2s_variant_regs;
|
const struct samsung_i2s_variant_regs *i2s_variant_regs;
|
||||||
};
|
};
|
||||||
|
@ -483,6 +477,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
|
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
|
||||||
u32 mod, mask, val = 0;
|
u32 mod, mask, val = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
|
|
||||||
spin_lock_irqsave(i2s->lock, flags);
|
spin_lock_irqsave(i2s->lock, flags);
|
||||||
mod = readl(i2s->addr + I2SMOD);
|
mod = readl(i2s->addr + I2SMOD);
|
||||||
|
@ -507,7 +504,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
&& (mod & cdcon_mask))))) {
|
&& (mod & cdcon_mask))))) {
|
||||||
dev_err(&i2s->pdev->dev,
|
dev_err(&i2s->pdev->dev,
|
||||||
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
||||||
return -EAGAIN;
|
ret = -EAGAIN;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dir == SND_SOC_CLOCK_IN)
|
if (dir == SND_SOC_CLOCK_IN)
|
||||||
|
@ -535,7 +533,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
} else {
|
} else {
|
||||||
i2s->rclk_srcrate =
|
i2s->rclk_srcrate =
|
||||||
clk_get_rate(i2s->op_clk);
|
clk_get_rate(i2s->op_clk);
|
||||||
return 0;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,8 +544,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
i2s->op_clk = clk_get(&i2s->pdev->dev,
|
i2s->op_clk = clk_get(&i2s->pdev->dev,
|
||||||
"i2s_opclk0");
|
"i2s_opclk0");
|
||||||
|
|
||||||
if (WARN_ON(IS_ERR(i2s->op_clk)))
|
if (WARN_ON(IS_ERR(i2s->op_clk))) {
|
||||||
return PTR_ERR(i2s->op_clk);
|
ret = PTR_ERR(i2s->op_clk);
|
||||||
|
i2s->op_clk = NULL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
clk_prepare_enable(i2s->op_clk);
|
clk_prepare_enable(i2s->op_clk);
|
||||||
i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
|
i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
|
||||||
|
@ -561,12 +562,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
|| (clk_id && !(mod & rsrc_mask))) {
|
|| (clk_id && !(mod & rsrc_mask))) {
|
||||||
dev_err(&i2s->pdev->dev,
|
dev_err(&i2s->pdev->dev,
|
||||||
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
||||||
return -EAGAIN;
|
ret = -EAGAIN;
|
||||||
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
/* Call can't be on the active DAI */
|
/* Call can't be on the active DAI */
|
||||||
i2s->op_clk = other->op_clk;
|
i2s->op_clk = other->op_clk;
|
||||||
i2s->rclk_srcrate = other->rclk_srcrate;
|
i2s->rclk_srcrate = other->rclk_srcrate;
|
||||||
return 0;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clk_id == 1)
|
if (clk_id == 1)
|
||||||
|
@ -574,7 +576,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&i2s->pdev->dev, "We don't serve that!\n");
|
dev_err(&i2s->pdev->dev, "We don't serve that!\n");
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(i2s->lock, flags);
|
spin_lock_irqsave(i2s->lock, flags);
|
||||||
|
@ -582,8 +585,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
|
||||||
mod = (mod & ~mask) | val;
|
mod = (mod & ~mask) | val;
|
||||||
writel(mod, i2s->addr + I2SMOD);
|
writel(mod, i2s->addr + I2SMOD);
|
||||||
spin_unlock_irqrestore(i2s->lock, flags);
|
spin_unlock_irqrestore(i2s->lock, flags);
|
||||||
|
done:
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
err:
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2s_set_fmt(struct snd_soc_dai *dai,
|
static int i2s_set_fmt(struct snd_soc_dai *dai,
|
||||||
|
@ -652,6 +660,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
spin_lock_irqsave(i2s->lock, flags);
|
spin_lock_irqsave(i2s->lock, flags);
|
||||||
mod = readl(i2s->addr + I2SMOD);
|
mod = readl(i2s->addr + I2SMOD);
|
||||||
/*
|
/*
|
||||||
|
@ -661,6 +670,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
|
||||||
if (any_active(i2s) &&
|
if (any_active(i2s) &&
|
||||||
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
|
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
|
||||||
spin_unlock_irqrestore(i2s->lock, flags);
|
spin_unlock_irqrestore(i2s->lock, flags);
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
dev_err(&i2s->pdev->dev,
|
dev_err(&i2s->pdev->dev,
|
||||||
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
@ -670,6 +680,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
|
||||||
mod |= tmp;
|
mod |= tmp;
|
||||||
writel(mod, i2s->addr + I2SMOD);
|
writel(mod, i2s->addr + I2SMOD);
|
||||||
spin_unlock_irqrestore(i2s->lock, flags);
|
spin_unlock_irqrestore(i2s->lock, flags);
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -681,6 +692,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
|
||||||
u32 mod, mask = 0, val = 0;
|
u32 mod, mask = 0, val = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
WARN_ON(!pm_runtime_active(dai->dev));
|
||||||
|
|
||||||
if (!is_secondary(i2s))
|
if (!is_secondary(i2s))
|
||||||
mask |= (MOD_DC2_EN | MOD_DC1_EN);
|
mask |= (MOD_DC2_EN | MOD_DC1_EN);
|
||||||
|
|
||||||
|
@ -769,6 +782,8 @@ static int i2s_startup(struct snd_pcm_substream *substream,
|
||||||
struct i2s_dai *other = get_other_dai(i2s);
|
struct i2s_dai *other = get_other_dai(i2s);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
|
|
||||||
spin_lock_irqsave(&lock, flags);
|
spin_lock_irqsave(&lock, flags);
|
||||||
|
|
||||||
i2s->mode |= DAI_OPENED;
|
i2s->mode |= DAI_OPENED;
|
||||||
|
@ -806,6 +821,8 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
|
||||||
i2s->bfs = 0;
|
i2s->bfs = 0;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&lock, flags);
|
spin_unlock_irqrestore(&lock, flags);
|
||||||
|
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_setup(struct i2s_dai *i2s)
|
static int config_setup(struct i2s_dai *i2s)
|
||||||
|
@ -880,6 +897,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
spin_lock_irqsave(i2s->lock, flags);
|
spin_lock_irqsave(i2s->lock, flags);
|
||||||
|
|
||||||
if (config_setup(i2s)) {
|
if (config_setup(i2s)) {
|
||||||
|
@ -908,6 +926,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(i2s->lock, flags);
|
spin_unlock_irqrestore(i2s->lock, flags);
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,13 +941,16 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
|
||||||
|
|
||||||
switch (div_id) {
|
switch (div_id) {
|
||||||
case SAMSUNG_I2S_DIV_BCLK:
|
case SAMSUNG_I2S_DIV_BCLK:
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
if ((any_active(i2s) && div && (get_bfs(i2s) != div))
|
if ((any_active(i2s) && div && (get_bfs(i2s) != div))
|
||||||
|| (other && other->bfs && (other->bfs != div))) {
|
|| (other && other->bfs && (other->bfs != div))) {
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
dev_err(&i2s->pdev->dev,
|
dev_err(&i2s->pdev->dev,
|
||||||
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
"%s:%d Other DAI busy\n", __func__, __LINE__);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
i2s->bfs = div;
|
i2s->bfs = div;
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(&i2s->pdev->dev,
|
dev_err(&i2s->pdev->dev,
|
||||||
|
@ -947,6 +969,8 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
|
||||||
snd_pcm_sframes_t delay;
|
snd_pcm_sframes_t delay;
|
||||||
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
|
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
|
||||||
|
|
||||||
|
WARN_ON(!pm_runtime_active(dai->dev));
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||||
delay = FIC_RXCOUNT(reg);
|
delay = FIC_RXCOUNT(reg);
|
||||||
else if (is_secondary(i2s))
|
else if (is_secondary(i2s))
|
||||||
|
@ -960,24 +984,12 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int i2s_suspend(struct snd_soc_dai *dai)
|
static int i2s_suspend(struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct i2s_dai *i2s = to_info(dai);
|
return pm_runtime_force_suspend(dai->dev);
|
||||||
|
|
||||||
i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
|
|
||||||
i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
|
|
||||||
i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2s_resume(struct snd_soc_dai *dai)
|
static int i2s_resume(struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct i2s_dai *i2s = to_info(dai);
|
return pm_runtime_force_resume(dai->dev);
|
||||||
|
|
||||||
writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
|
|
||||||
writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
|
|
||||||
writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define i2s_suspend NULL
|
#define i2s_suspend NULL
|
||||||
|
@ -990,6 +1002,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
|
||||||
struct i2s_dai *other = get_other_dai(i2s);
|
struct i2s_dai *other = get_other_dai(i2s);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
|
|
||||||
if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
|
if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
|
||||||
snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,
|
snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -1022,6 +1036,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
|
||||||
if (!is_opened(other))
|
if (!is_opened(other))
|
||||||
i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
|
i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
|
||||||
0, SND_SOC_CLOCK_IN);
|
0, SND_SOC_CLOCK_IN);
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1031,6 +1046,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
|
||||||
struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
|
struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dai->dev);
|
||||||
|
|
||||||
if (!is_secondary(i2s)) {
|
if (!is_secondary(i2s)) {
|
||||||
if (i2s->quirks & QUIRK_NEED_RSTCLR) {
|
if (i2s->quirks & QUIRK_NEED_RSTCLR) {
|
||||||
spin_lock_irqsave(i2s->lock, flags);
|
spin_lock_irqsave(i2s->lock, flags);
|
||||||
|
@ -1039,6 +1056,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_put(dai->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1066,7 +1085,6 @@ static const struct snd_soc_component_driver samsung_i2s_component = {
|
||||||
static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
|
static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
|
||||||
{
|
{
|
||||||
struct i2s_dai *i2s;
|
struct i2s_dai *i2s;
|
||||||
int ret;
|
|
||||||
|
|
||||||
i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
|
i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
|
||||||
if (i2s == NULL)
|
if (i2s == NULL)
|
||||||
|
@ -1091,33 +1109,21 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
|
||||||
i2s->i2s_dai_drv.capture.channels_max = 2;
|
i2s->i2s_dai_drv.capture.channels_max = 2;
|
||||||
i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
|
i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;
|
||||||
i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
|
i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
|
||||||
dev_set_drvdata(&i2s->pdev->dev, i2s);
|
|
||||||
} else { /* Create a new platform_device for Secondary */
|
|
||||||
i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1);
|
|
||||||
if (!i2s->pdev)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
i2s->pdev->dev.parent = &pdev->dev;
|
|
||||||
|
|
||||||
platform_set_drvdata(i2s->pdev, i2s);
|
|
||||||
ret = platform_device_add(i2s->pdev);
|
|
||||||
if (ret < 0)
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return i2s;
|
return i2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2s_free_sec_dai(struct i2s_dai *i2s)
|
|
||||||
{
|
|
||||||
platform_device_del(i2s->pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int i2s_runtime_suspend(struct device *dev)
|
static int i2s_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2s_dai *i2s = dev_get_drvdata(dev);
|
struct i2s_dai *i2s = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
|
||||||
|
i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
|
||||||
|
i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
|
||||||
|
|
||||||
|
if (i2s->op_clk)
|
||||||
|
clk_disable_unprepare(i2s->op_clk);
|
||||||
clk_disable_unprepare(i2s->clk);
|
clk_disable_unprepare(i2s->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1128,6 +1134,12 @@ static int i2s_runtime_resume(struct device *dev)
|
||||||
struct i2s_dai *i2s = dev_get_drvdata(dev);
|
struct i2s_dai *i2s = dev_get_drvdata(dev);
|
||||||
|
|
||||||
clk_prepare_enable(i2s->clk);
|
clk_prepare_enable(i2s->clk);
|
||||||
|
if (i2s->op_clk)
|
||||||
|
clk_prepare_enable(i2s->op_clk);
|
||||||
|
|
||||||
|
writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
|
||||||
|
writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
|
||||||
|
writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1179,13 +1191,13 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
|
||||||
u32 val = readl(i2s->addr + I2SPSR);
|
u32 val = readl(i2s->addr + I2SPSR);
|
||||||
writel(val | PSR_PSREN, i2s->addr + I2SPSR);
|
writel(val | PSR_PSREN, i2s->addr + I2SPSR);
|
||||||
|
|
||||||
i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL,
|
i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
|
||||||
"i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
|
"i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
|
||||||
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
|
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
|
||||||
i2s->addr + I2SMOD, reg_info->rclksrc_off,
|
i2s->addr + I2SMOD, reg_info->rclksrc_off,
|
||||||
1, 0, i2s->lock);
|
1, 0, i2s->lock);
|
||||||
|
|
||||||
i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL,
|
i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
|
||||||
"i2s_presc", "i2s_rclksrc",
|
"i2s_presc", "i2s_rclksrc",
|
||||||
CLK_SET_RATE_PARENT,
|
CLK_SET_RATE_PARENT,
|
||||||
i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
|
i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
|
||||||
|
@ -1196,7 +1208,7 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
|
||||||
of_property_read_string_index(dev->of_node,
|
of_property_read_string_index(dev->of_node,
|
||||||
"clock-output-names", 0, &clk_name[0]);
|
"clock-output-names", 0, &clk_name[0]);
|
||||||
|
|
||||||
i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0],
|
i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0],
|
||||||
p_names[0], CLK_SET_RATE_PARENT,
|
p_names[0], CLK_SET_RATE_PARENT,
|
||||||
i2s->addr + I2SMOD, reg_info->cdclkcon_off,
|
i2s->addr + I2SMOD, reg_info->cdclkcon_off,
|
||||||
CLK_GATE_SET_TO_DISABLE, i2s->lock);
|
CLK_GATE_SET_TO_DISABLE, i2s->lock);
|
||||||
|
@ -1218,7 +1230,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct i2s_dai *pri_dai, *sec_dai = NULL;
|
struct i2s_dai *pri_dai, *sec_dai = NULL;
|
||||||
struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
|
struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
|
||||||
struct samsung_i2s *i2s_cfg = NULL;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
u32 regs_base, quirks = 0, idma_addr = 0;
|
u32 regs_base, quirks = 0, idma_addr = 0;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
@ -1231,22 +1242,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
i2s_dai_data = (struct samsung_i2s_dai_data *)
|
i2s_dai_data = (struct samsung_i2s_dai_data *)
|
||||||
platform_get_device_id(pdev)->driver_data;
|
platform_get_device_id(pdev)->driver_data;
|
||||||
|
|
||||||
/* Call during the secondary interface registration */
|
|
||||||
if (i2s_dai_data->dai_type == TYPE_SEC) {
|
|
||||||
sec_dai = dev_get_drvdata(&pdev->dev);
|
|
||||||
if (!sec_dai) {
|
|
||||||
dev_err(&pdev->dev, "Unable to get drvdata\n");
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
ret = samsung_asoc_dma_platform_register(&pdev->dev,
|
|
||||||
sec_dai->filter, "tx-sec", NULL);
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return devm_snd_soc_register_component(&sec_dai->pdev->dev,
|
|
||||||
&samsung_i2s_component,
|
|
||||||
&sec_dai->i2s_dai_drv, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pri_dai = i2s_alloc_dai(pdev, false);
|
pri_dai = i2s_alloc_dai(pdev, false);
|
||||||
if (!pri_dai) {
|
if (!pri_dai) {
|
||||||
|
@ -1267,13 +1262,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
|
pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
|
||||||
pri_dai->filter = i2s_pdata->dma_filter;
|
pri_dai->filter = i2s_pdata->dma_filter;
|
||||||
|
|
||||||
if (&i2s_pdata->type)
|
quirks = i2s_pdata->type.quirks;
|
||||||
i2s_cfg = &i2s_pdata->type.i2s;
|
idma_addr = i2s_pdata->type.idma_addr;
|
||||||
|
|
||||||
if (i2s_cfg) {
|
|
||||||
quirks = i2s_cfg->quirks;
|
|
||||||
idma_addr = i2s_cfg->idma_addr;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
quirks = i2s_dai_data->quirks;
|
quirks = i2s_dai_data->quirks;
|
||||||
if (of_property_read_u32(np, "samsung,idma-addr",
|
if (of_property_read_u32(np, "samsung,idma-addr",
|
||||||
|
@ -1320,6 +1310,12 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_disable_clk;
|
goto err_disable_clk;
|
||||||
|
|
||||||
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||||
|
&samsung_i2s_component,
|
||||||
|
&pri_dai->i2s_dai_drv, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_disable_clk;
|
||||||
|
|
||||||
if (quirks & QUIRK_SEC_DAI) {
|
if (quirks & QUIRK_SEC_DAI) {
|
||||||
sec_dai = i2s_alloc_dai(pdev, true);
|
sec_dai = i2s_alloc_dai(pdev, true);
|
||||||
if (!sec_dai) {
|
if (!sec_dai) {
|
||||||
|
@ -1345,6 +1341,17 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
sec_dai->idma_playback.addr = idma_addr;
|
sec_dai->idma_playback.addr = idma_addr;
|
||||||
sec_dai->pri_dai = pri_dai;
|
sec_dai->pri_dai = pri_dai;
|
||||||
pri_dai->sec_dai = sec_dai;
|
pri_dai->sec_dai = sec_dai;
|
||||||
|
|
||||||
|
ret = samsung_asoc_dma_platform_register(&pdev->dev,
|
||||||
|
sec_dai->filter, "tx-sec", NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_disable_clk;
|
||||||
|
|
||||||
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||||
|
&samsung_i2s_component,
|
||||||
|
&sec_dai->i2s_dai_drv, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
|
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
|
||||||
|
@ -1353,13 +1360,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
goto err_disable_clk;
|
goto err_disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_snd_soc_register_component(&pri_dai->pdev->dev,
|
dev_set_drvdata(&pdev->dev, pri_dai);
|
||||||
&samsung_i2s_component,
|
|
||||||
&pri_dai->i2s_dai_drv, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err_free_dai;
|
|
||||||
|
|
||||||
|
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
ret = i2s_register_clock_provider(pdev);
|
ret = i2s_register_clock_provider(pdev);
|
||||||
|
@ -1367,9 +1370,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
err_free_dai:
|
|
||||||
if (sec_dai)
|
|
||||||
i2s_free_sec_dai(sec_dai);
|
|
||||||
err_disable_clk:
|
err_disable_clk:
|
||||||
clk_disable_unprepare(pri_dai->clk);
|
clk_disable_unprepare(pri_dai->clk);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1377,25 +1377,20 @@ err_disable_clk:
|
||||||
|
|
||||||
static int samsung_i2s_remove(struct platform_device *pdev)
|
static int samsung_i2s_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct i2s_dai *i2s, *other;
|
struct i2s_dai *pri_dai, *sec_dai;
|
||||||
|
|
||||||
i2s = dev_get_drvdata(&pdev->dev);
|
pri_dai = dev_get_drvdata(&pdev->dev);
|
||||||
other = get_other_dai(i2s);
|
sec_dai = pri_dai->sec_dai;
|
||||||
|
|
||||||
if (other) {
|
pri_dai->sec_dai = NULL;
|
||||||
other->pri_dai = NULL;
|
sec_dai->pri_dai = NULL;
|
||||||
other->sec_dai = NULL;
|
|
||||||
} else {
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_secondary(i2s)) {
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
i2s_unregister_clock_provider(pdev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
clk_disable_unprepare(i2s->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
i2s->pri_dai = NULL;
|
i2s_unregister_clock_provider(pdev);
|
||||||
i2s->sec_dai = NULL;
|
clk_disable_unprepare(pri_dai->clk);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1457,49 +1452,37 @@ static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
|
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
|
||||||
.dai_type = TYPE_PRI,
|
|
||||||
.quirks = QUIRK_NO_MUXPSR,
|
.quirks = QUIRK_NO_MUXPSR,
|
||||||
.i2s_variant_regs = &i2sv3_regs,
|
.i2s_variant_regs = &i2sv3_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_i2s_dai_data i2sv5_dai_type = {
|
static const struct samsung_i2s_dai_data i2sv5_dai_type = {
|
||||||
.dai_type = TYPE_PRI,
|
|
||||||
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
|
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
|
||||||
QUIRK_SUPPORTS_IDMA,
|
QUIRK_SUPPORTS_IDMA,
|
||||||
.i2s_variant_regs = &i2sv3_regs,
|
.i2s_variant_regs = &i2sv3_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_i2s_dai_data i2sv6_dai_type = {
|
static const struct samsung_i2s_dai_data i2sv6_dai_type = {
|
||||||
.dai_type = TYPE_PRI,
|
|
||||||
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
|
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
|
||||||
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
|
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
|
||||||
.i2s_variant_regs = &i2sv6_regs,
|
.i2s_variant_regs = &i2sv6_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_i2s_dai_data i2sv7_dai_type = {
|
static const struct samsung_i2s_dai_data i2sv7_dai_type = {
|
||||||
.dai_type = TYPE_PRI,
|
|
||||||
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
|
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
|
||||||
QUIRK_SUPPORTS_TDM,
|
QUIRK_SUPPORTS_TDM,
|
||||||
.i2s_variant_regs = &i2sv7_regs,
|
.i2s_variant_regs = &i2sv7_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
|
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
|
||||||
.dai_type = TYPE_PRI,
|
|
||||||
.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
|
.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
|
||||||
.i2s_variant_regs = &i2sv5_i2s1_regs,
|
.i2s_variant_regs = &i2sv5_i2s1_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
|
|
||||||
.dai_type = TYPE_SEC,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct platform_device_id samsung_i2s_driver_ids[] = {
|
static const struct platform_device_id samsung_i2s_driver_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "samsung-i2s",
|
.name = "samsung-i2s",
|
||||||
.driver_data = (kernel_ulong_t)&i2sv3_dai_type,
|
.driver_data = (kernel_ulong_t)&i2sv3_dai_type,
|
||||||
}, {
|
|
||||||
.name = "samsung-i2s-sec",
|
|
||||||
.driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
|
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
@ -1531,6 +1514,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match);
|
||||||
static const struct dev_pm_ops samsung_i2s_pm = {
|
static const struct dev_pm_ops samsung_i2s_pm = {
|
||||||
SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
|
SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
|
||||||
i2s_runtime_resume, NULL)
|
i2s_runtime_resume, NULL)
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||||
|
pm_runtime_force_resume)
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_driver samsung_i2s_driver = {
|
static struct platform_driver samsung_i2s_driver = {
|
||||||
|
|
|
@ -32,14 +32,11 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||||
unsigned int pll_out;
|
unsigned int pll_out;
|
||||||
int bfs, rfs, ret;
|
int rfs, ret;
|
||||||
|
|
||||||
switch (params_width(params)) {
|
switch (params_width(params)) {
|
||||||
case 8:
|
case 8:
|
||||||
bfs = 16;
|
|
||||||
break;
|
|
||||||
case 16:
|
case 16:
|
||||||
bfs = 32;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
|
|
|
@ -9,9 +9,20 @@ config SND_SUN4I_CODEC
|
||||||
Select Y or M to add support for the Codec embedded in the Allwinner
|
Select Y or M to add support for the Codec embedded in the Allwinner
|
||||||
A10 and affiliated SoCs.
|
A10 and affiliated SoCs.
|
||||||
|
|
||||||
|
config SND_SUN8I_CODEC
|
||||||
|
tristate "Allwinner SUN8I audio codec"
|
||||||
|
depends on OF
|
||||||
|
depends on MACH_SUN8I || COMPILE_TEST
|
||||||
|
select REGMAP_MMIO
|
||||||
|
help
|
||||||
|
This option enables the digital part of the internal audio codec for
|
||||||
|
Allwinner sun8i SoC (and particularly A33).
|
||||||
|
|
||||||
|
Say Y or M if you want to add sun8i digital audio codec support.
|
||||||
|
|
||||||
config SND_SUN8I_CODEC_ANALOG
|
config SND_SUN8I_CODEC_ANALOG
|
||||||
tristate "Allwinner sun8i Codec Analog Controls Support"
|
tristate "Allwinner sun8i Codec Analog Controls Support"
|
||||||
depends on MACH_SUN8I || COMPILE_TEST
|
depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
|
||||||
select REGMAP
|
select REGMAP
|
||||||
help
|
help
|
||||||
Say Y or M if you want to add support for the analog controls for
|
Say Y or M if you want to add support for the analog controls for
|
||||||
|
|
|
@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
|
||||||
obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
|
obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
|
||||||
obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
|
obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
|
||||||
obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
|
obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
|
||||||
|
obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o
|
||||||
|
|
|
@ -1058,6 +1058,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
|
||||||
{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
|
{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
|
||||||
{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
|
{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
|
||||||
{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
|
{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
|
||||||
|
{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },
|
||||||
{ "LINEOUT", NULL, "Line Out Source Playback Route" },
|
{ "LINEOUT", NULL, "Line Out Source Playback Route" },
|
||||||
|
|
||||||
/* ADC Routes */
|
/* ADC Routes */
|
||||||
|
|
|
@ -14,9 +14,11 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
|
|
||||||
#include <sound/dmaengine_pcm.h>
|
#include <sound/dmaengine_pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
|
@ -92,6 +94,7 @@ struct sun4i_i2s {
|
||||||
struct clk *bus_clk;
|
struct clk *bus_clk;
|
||||||
struct clk *mod_clk;
|
struct clk *mod_clk;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
struct reset_control *rst;
|
||||||
|
|
||||||
unsigned int mclk_freq;
|
unsigned int mclk_freq;
|
||||||
|
|
||||||
|
@ -651,9 +654,22 @@ static int sun4i_i2s_runtime_suspend(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sun4i_i2s_quirks {
|
||||||
|
bool has_reset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
|
||||||
|
.has_reset = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
|
||||||
|
.has_reset = true,
|
||||||
|
};
|
||||||
|
|
||||||
static int sun4i_i2s_probe(struct platform_device *pdev)
|
static int sun4i_i2s_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sun4i_i2s *i2s;
|
struct sun4i_i2s *i2s;
|
||||||
|
const struct sun4i_i2s_quirks *quirks;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
|
@ -674,6 +690,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quirks = of_device_get_match_data(&pdev->dev);
|
||||||
|
if (!quirks) {
|
||||||
|
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
|
i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
|
||||||
if (IS_ERR(i2s->bus_clk)) {
|
if (IS_ERR(i2s->bus_clk)) {
|
||||||
dev_err(&pdev->dev, "Can't get our bus clock\n");
|
dev_err(&pdev->dev, "Can't get our bus clock\n");
|
||||||
|
@ -692,7 +714,24 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev, "Can't get our mod clock\n");
|
dev_err(&pdev->dev, "Can't get our mod clock\n");
|
||||||
return PTR_ERR(i2s->mod_clk);
|
return PTR_ERR(i2s->mod_clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quirks->has_reset) {
|
||||||
|
i2s->rst = devm_reset_control_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(i2s->rst)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to get reset control\n");
|
||||||
|
return PTR_ERR(i2s->rst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_ERR(i2s->rst)) {
|
||||||
|
ret = reset_control_deassert(i2s->rst);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Failed to deassert the reset control\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
|
i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
|
||||||
i2s->playback_dma_data.maxburst = 8;
|
i2s->playback_dma_data.maxburst = 8;
|
||||||
|
|
||||||
|
@ -727,23 +766,37 @@ err_suspend:
|
||||||
sun4i_i2s_runtime_suspend(&pdev->dev);
|
sun4i_i2s_runtime_suspend(&pdev->dev);
|
||||||
err_pm_disable:
|
err_pm_disable:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
if (!IS_ERR(i2s->rst))
|
||||||
|
reset_control_assert(i2s->rst);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun4i_i2s_remove(struct platform_device *pdev)
|
static int sun4i_i2s_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
snd_dmaengine_pcm_unregister(&pdev->dev);
|
snd_dmaengine_pcm_unregister(&pdev->dev);
|
||||||
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||||
sun4i_i2s_runtime_suspend(&pdev->dev);
|
sun4i_i2s_runtime_suspend(&pdev->dev);
|
||||||
|
|
||||||
|
if (!IS_ERR(i2s->rst))
|
||||||
|
reset_control_assert(i2s->rst);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id sun4i_i2s_match[] = {
|
static const struct of_device_id sun4i_i2s_match[] = {
|
||||||
{ .compatible = "allwinner,sun4i-a10-i2s", },
|
{
|
||||||
|
.compatible = "allwinner,sun4i-a10-i2s",
|
||||||
|
.data = &sun4i_a10_i2s_quirks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "allwinner,sun6i-a31-i2s",
|
||||||
|
.data = &sun6i_a31_i2s_quirks,
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
|
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
|
||||||
|
|
|
@ -103,6 +103,8 @@
|
||||||
#define SUN4I_SPDIF_ISTA_RXOSTA BIT(1)
|
#define SUN4I_SPDIF_ISTA_RXOSTA BIT(1)
|
||||||
#define SUN4I_SPDIF_ISTA_RXASTA BIT(0)
|
#define SUN4I_SPDIF_ISTA_RXASTA BIT(0)
|
||||||
|
|
||||||
|
#define SUN8I_SPDIF_TXFIFO (0x20)
|
||||||
|
|
||||||
#define SUN4I_SPDIF_TXCNT (0x24)
|
#define SUN4I_SPDIF_TXCNT (0x24)
|
||||||
|
|
||||||
#define SUN4I_SPDIF_RXCNT (0x28)
|
#define SUN4I_SPDIF_RXCNT (0x28)
|
||||||
|
@ -403,17 +405,38 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = {
|
||||||
.name = "spdif",
|
.name = "spdif",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget dit_widgets[] = {
|
struct sun4i_spdif_quirks {
|
||||||
SND_SOC_DAPM_OUTPUT("spdif-out"),
|
unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
|
||||||
|
bool has_reset;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_dapm_route dit_routes[] = {
|
static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = {
|
||||||
{ "spdif-out", NULL, "Playback" },
|
.reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = {
|
||||||
|
.reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
|
||||||
|
.has_reset = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = {
|
||||||
|
.reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
|
||||||
|
.has_reset = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id sun4i_spdif_of_match[] = {
|
static const struct of_device_id sun4i_spdif_of_match[] = {
|
||||||
{ .compatible = "allwinner,sun4i-a10-spdif", },
|
{
|
||||||
{ .compatible = "allwinner,sun6i-a31-spdif", },
|
.compatible = "allwinner,sun4i-a10-spdif",
|
||||||
|
.data = &sun4i_a10_spdif_quirks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "allwinner,sun6i-a31-spdif",
|
||||||
|
.data = &sun6i_a31_spdif_quirks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "allwinner,sun8i-h3-spdif",
|
||||||
|
.data = &sun8i_h3_spdif_quirks,
|
||||||
|
},
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
|
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
|
||||||
|
@ -446,6 +469,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sun4i_spdif_dev *host;
|
struct sun4i_spdif_dev *host;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
const struct sun4i_spdif_quirks *quirks;
|
||||||
int ret;
|
int ret;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
||||||
|
@ -467,6 +491,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(base))
|
if (IS_ERR(base))
|
||||||
return PTR_ERR(base);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
quirks = of_device_get_match_data(&pdev->dev);
|
||||||
|
if (quirks == NULL) {
|
||||||
|
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||||
&sun4i_spdif_regmap_config);
|
&sun4i_spdif_regmap_config);
|
||||||
|
|
||||||
|
@ -480,23 +510,21 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
|
||||||
host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
|
host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
|
||||||
if (IS_ERR(host->spdif_clk)) {
|
if (IS_ERR(host->spdif_clk)) {
|
||||||
dev_err(&pdev->dev, "failed to get a spdif clock.\n");
|
dev_err(&pdev->dev, "failed to get a spdif clock.\n");
|
||||||
ret = PTR_ERR(host->spdif_clk);
|
return PTR_ERR(host->spdif_clk);
|
||||||
goto err_disable_apb_clk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
|
host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata;
|
||||||
host->dma_params_tx.maxburst = 8;
|
host->dma_params_tx.maxburst = 8;
|
||||||
host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
if (of_device_is_compatible(pdev->dev.of_node,
|
if (quirks->has_reset) {
|
||||||
"allwinner,sun6i-a31-spdif")) {
|
|
||||||
host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
|
host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
|
||||||
if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
|
if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
|
||||||
goto err_disable_apb_clk;
|
return ret;
|
||||||
}
|
}
|
||||||
if (!IS_ERR(host->rst))
|
if (!IS_ERR(host->rst))
|
||||||
reset_control_deassert(host->rst);
|
reset_control_deassert(host->rst);
|
||||||
|
@ -505,7 +533,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
|
||||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||||
&sun4i_spdif_component, &sun4i_spdif_dai, 1);
|
&sun4i_spdif_component, &sun4i_spdif_dai, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_disable_apb_clk;
|
return ret;
|
||||||
|
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||||
|
@ -523,9 +551,6 @@ err_suspend:
|
||||||
sun4i_spdif_runtime_suspend(&pdev->dev);
|
sun4i_spdif_runtime_suspend(&pdev->dev);
|
||||||
err_unregister:
|
err_unregister:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
snd_soc_unregister_component(&pdev->dev);
|
|
||||||
err_disable_apb_clk:
|
|
||||||
clk_disable_unprepare(host->apb_clk);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,9 +560,6 @@ static int sun4i_spdif_remove(struct platform_device *pdev)
|
||||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||||
sun4i_spdif_runtime_suspend(&pdev->dev);
|
sun4i_spdif_runtime_suspend(&pdev->dev);
|
||||||
|
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
|
||||||
snd_soc_unregister_component(&pdev->dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -398,11 +398,37 @@ static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {
|
||||||
sun8i_codec_hp_src_enum),
|
sun8i_codec_hp_src_enum),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,
|
||||||
|
struct snd_kcontrol *k, int event)
|
||||||
|
{
|
||||||
|
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||||
|
|
||||||
|
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||||
|
snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
|
||||||
|
BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
|
||||||
|
BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));
|
||||||
|
/*
|
||||||
|
* Need a delay to have the amplifier up. 700ms seems the best
|
||||||
|
* compromise between the time to let the amplifier up and the
|
||||||
|
* time not to feel this delay while playing a sound.
|
||||||
|
*/
|
||||||
|
msleep(700);
|
||||||
|
} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
|
||||||
|
snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,
|
||||||
|
BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),
|
||||||
|
0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
|
static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {
|
||||||
SND_SOC_DAPM_MUX("Headphone Source Playback Route",
|
SND_SOC_DAPM_MUX("Headphone Source Playback Route",
|
||||||
SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
|
SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),
|
||||||
SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
|
SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,
|
||||||
SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0),
|
SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,
|
||||||
|
sun8i_headphone_amp_event,
|
||||||
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||||
SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
|
SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,
|
||||||
SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
|
SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),
|
||||||
SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
|
SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,
|
||||||
|
|
|
@ -0,0 +1,498 @@
|
||||||
|
/*
|
||||||
|
* This driver supports the digital controls for the internal codec
|
||||||
|
* found in Allwinner's A33 SoCs.
|
||||||
|
*
|
||||||
|
* (C) Copyright 2010-2016
|
||||||
|
* Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
|
||||||
|
* huangxin <huangxin@Reuuimllatech.com>
|
||||||
|
* Mylène Josserand <mylene.josserand@free-electrons.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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/delay.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
#include <sound/soc.h>
|
||||||
|
#include <sound/soc-dapm.h>
|
||||||
|
|
||||||
|
#define SUN8I_SYSCLK_CTL 0x00c
|
||||||
|
#define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11
|
||||||
|
#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL 9
|
||||||
|
#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC 8
|
||||||
|
#define SUN8I_SYSCLK_CTL_SYSCLK_ENA 3
|
||||||
|
#define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0
|
||||||
|
#define SUN8I_MOD_CLK_ENA 0x010
|
||||||
|
#define SUN8I_MOD_CLK_ENA_AIF1 15
|
||||||
|
#define SUN8I_MOD_CLK_ENA_DAC 2
|
||||||
|
#define SUN8I_MOD_RST_CTL 0x014
|
||||||
|
#define SUN8I_MOD_RST_CTL_AIF1 15
|
||||||
|
#define SUN8I_MOD_RST_CTL_DAC 2
|
||||||
|
#define SUN8I_SYS_SR_CTRL 0x018
|
||||||
|
#define SUN8I_SYS_SR_CTRL_AIF1_FS 12
|
||||||
|
#define SUN8I_SYS_SR_CTRL_AIF2_FS 8
|
||||||
|
#define SUN8I_AIF1CLK_CTRL 0x040
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD 15
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV 14
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV 13
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV 9
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV 6
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16 (1 << 6)
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4)
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2
|
||||||
|
#define SUN8I_AIF1_DACDAT_CTRL 0x048
|
||||||
|
#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15
|
||||||
|
#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14
|
||||||
|
#define SUN8I_DAC_DIG_CTRL 0x120
|
||||||
|
#define SUN8I_DAC_DIG_CTRL_ENDA 15
|
||||||
|
#define SUN8I_DAC_MXR_SRC 0x130
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL 13
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL 12
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R 11
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R 10
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9
|
||||||
|
#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR 8
|
||||||
|
|
||||||
|
#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12)
|
||||||
|
#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8)
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4)
|
||||||
|
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6)
|
||||||
|
|
||||||
|
struct sun8i_codec {
|
||||||
|
struct device *dev;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct clk *clk_module;
|
||||||
|
struct clk *clk_bus;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sun8i_codec_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sun8i_codec *scodec = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(scodec->clk_module);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to enable the module clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(scodec->clk_bus);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to enable the bus clock\n");
|
||||||
|
goto err_disable_modclk;
|
||||||
|
}
|
||||||
|
|
||||||
|
regcache_cache_only(scodec->regmap, false);
|
||||||
|
|
||||||
|
ret = regcache_sync(scodec->regmap);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to sync regmap cache\n");
|
||||||
|
goto err_disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_disable_clk:
|
||||||
|
clk_disable_unprepare(scodec->clk_bus);
|
||||||
|
|
||||||
|
err_disable_modclk:
|
||||||
|
clk_disable_unprepare(scodec->clk_module);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun8i_codec_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct sun8i_codec *scodec = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
regcache_cache_only(scodec->regmap, true);
|
||||||
|
regcache_mark_dirty(scodec->regmap);
|
||||||
|
|
||||||
|
clk_disable_unprepare(scodec->clk_module);
|
||||||
|
clk_disable_unprepare(scodec->clk_bus);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
|
||||||
|
{
|
||||||
|
unsigned int rate = params_rate(params);
|
||||||
|
|
||||||
|
switch (rate) {
|
||||||
|
case 8000:
|
||||||
|
case 7350:
|
||||||
|
return 0x0;
|
||||||
|
case 11025:
|
||||||
|
return 0x1;
|
||||||
|
case 12000:
|
||||||
|
return 0x2;
|
||||||
|
case 16000:
|
||||||
|
return 0x3;
|
||||||
|
case 22050:
|
||||||
|
return 0x4;
|
||||||
|
case 24000:
|
||||||
|
return 0x5;
|
||||||
|
case 32000:
|
||||||
|
return 0x6;
|
||||||
|
case 44100:
|
||||||
|
return 0x7;
|
||||||
|
case 48000:
|
||||||
|
return 0x8;
|
||||||
|
case 96000:
|
||||||
|
return 0x9;
|
||||||
|
case 192000:
|
||||||
|
return 0xa;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||||
|
{
|
||||||
|
struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec);
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
/* clock masters */
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFS: /* DAI Slave */
|
||||||
|
value = 0x0; /* Codec Master */
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_CBM_CFM: /* DAI Master */
|
||||||
|
value = 0x1; /* Codec Slave */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||||
|
BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
|
||||||
|
value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
|
||||||
|
|
||||||
|
/* clock inversion */
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_NB_NF: /* Normal */
|
||||||
|
value = 0x0;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_IB_IF: /* Inversion */
|
||||||
|
value = 0x1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||||
|
BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
|
||||||
|
value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||||
|
BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
|
||||||
|
value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
|
||||||
|
|
||||||
|
/* DAI format */
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_I2S:
|
||||||
|
value = 0x0;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_LEFT_J:
|
||||||
|
value = 0x1;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_RIGHT_J:
|
||||||
|
value = 0x2;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_DSP_A:
|
||||||
|
case SND_SOC_DAIFMT_DSP_B:
|
||||||
|
value = 0x3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||||
|
BIT(SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT),
|
||||||
|
value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *params,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec);
|
||||||
|
int sample_rate;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The CPU DAI handles only a sample of 16 bits. Configure the
|
||||||
|
* codec to handle this type of sample resolution.
|
||||||
|
*/
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||||
|
SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
|
||||||
|
SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
|
||||||
|
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
|
||||||
|
SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
|
||||||
|
SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16);
|
||||||
|
|
||||||
|
sample_rate = sun8i_codec_get_hw_rate(params);
|
||||||
|
if (sample_rate < 0)
|
||||||
|
return sample_rate;
|
||||||
|
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
|
||||||
|
SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
|
||||||
|
sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
|
||||||
|
regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
|
||||||
|
SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
|
||||||
|
sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new sun8i_output_left_mixer_controls[] = {
|
||||||
|
SOC_DAPM_SINGLE("LSlot 0", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L, 1, 0),
|
||||||
|
SOC_DAPM_SINGLE("LSlot 1", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L, 1, 0),
|
||||||
|
SOC_DAPM_SINGLE("DACL", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL, 1, 0),
|
||||||
|
SOC_DAPM_SINGLE("ADCL", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL, 1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new sun8i_output_right_mixer_controls[] = {
|
||||||
|
SOC_DAPM_SINGLE("RSlot 0", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
|
||||||
|
SOC_DAPM_SINGLE("RSlot 1", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
|
||||||
|
SOC_DAPM_SINGLE("DACR", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
|
||||||
|
SOC_DAPM_SINGLE("ADCR", SUN8I_DAC_MXR_SRC,
|
||||||
|
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
|
||||||
|
/* Digital parts of the DACs */
|
||||||
|
SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
|
||||||
|
0, NULL, 0),
|
||||||
|
|
||||||
|
/* Analog DAC */
|
||||||
|
SND_SOC_DAPM_DAC("Digital Left DAC", "Playback", SUN8I_AIF1_DACDAT_CTRL,
|
||||||
|
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
|
||||||
|
SND_SOC_DAPM_DAC("Digital Right DAC", "Playback", SUN8I_AIF1_DACDAT_CTRL,
|
||||||
|
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
|
||||||
|
|
||||||
|
/* DAC Mixers */
|
||||||
|
SND_SOC_DAPM_MIXER("Left DAC Mixer", SND_SOC_NOPM, 0, 0,
|
||||||
|
sun8i_output_left_mixer_controls,
|
||||||
|
ARRAY_SIZE(sun8i_output_left_mixer_controls)),
|
||||||
|
SND_SOC_DAPM_MIXER("Right DAC Mixer", SND_SOC_NOPM, 0, 0,
|
||||||
|
sun8i_output_right_mixer_controls,
|
||||||
|
ARRAY_SIZE(sun8i_output_right_mixer_controls)),
|
||||||
|
|
||||||
|
/* Clocks */
|
||||||
|
SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA,
|
||||||
|
SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
|
||||||
|
SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA,
|
||||||
|
SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
|
||||||
|
SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL,
|
||||||
|
SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
|
||||||
|
SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
|
||||||
|
SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
|
||||||
|
|
||||||
|
SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL,
|
||||||
|
SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0),
|
||||||
|
/* Inversion as 0=AIF1, 1=AIF2 */
|
||||||
|
SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL,
|
||||||
|
SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0),
|
||||||
|
|
||||||
|
/* Module reset */
|
||||||
|
SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL,
|
||||||
|
SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
|
||||||
|
SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL,
|
||||||
|
SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
|
||||||
|
|
||||||
|
SND_SOC_DAPM_OUTPUT("HP"),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
|
||||||
|
/* Clock Routes */
|
||||||
|
{ "AIF1", NULL, "SYSCLK AIF1" },
|
||||||
|
{ "AIF1 PLL", NULL, "AIF1" },
|
||||||
|
{ "RST AIF1", NULL, "AIF1 PLL" },
|
||||||
|
{ "MODCLK AFI1", NULL, "RST AIF1" },
|
||||||
|
{ "DAC", NULL, "MODCLK AFI1" },
|
||||||
|
|
||||||
|
{ "RST DAC", NULL, "SYSCLK" },
|
||||||
|
{ "MODCLK DAC", NULL, "RST DAC" },
|
||||||
|
{ "DAC", NULL, "MODCLK DAC" },
|
||||||
|
|
||||||
|
/* DAC Routes */
|
||||||
|
{ "Digital Left DAC", NULL, "DAC" },
|
||||||
|
{ "Digital Right DAC", NULL, "DAC" },
|
||||||
|
|
||||||
|
/* DAC Mixer Routes */
|
||||||
|
{ "Left DAC Mixer", "LSlot 0", "Digital Left DAC"},
|
||||||
|
{ "Right DAC Mixer", "RSlot 0", "Digital Right DAC"},
|
||||||
|
|
||||||
|
/* End of route : HP out */
|
||||||
|
{ "HP", NULL, "Left DAC Mixer" },
|
||||||
|
{ "HP", NULL, "Right DAC Mixer" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_dai_ops sun8i_codec_dai_ops = {
|
||||||
|
.hw_params = sun8i_codec_hw_params,
|
||||||
|
.set_fmt = sun8i_set_fmt,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_dai_driver sun8i_codec_dai = {
|
||||||
|
.name = "sun8i",
|
||||||
|
/* playback capabilities */
|
||||||
|
.playback = {
|
||||||
|
.stream_name = "Playback",
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 2,
|
||||||
|
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||||
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||||
|
},
|
||||||
|
/* pcm operations */
|
||||||
|
.ops = &sun8i_codec_dai_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct snd_soc_codec_driver sun8i_soc_codec = {
|
||||||
|
.component_driver = {
|
||||||
|
.dapm_widgets = sun8i_codec_dapm_widgets,
|
||||||
|
.num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets),
|
||||||
|
.dapm_routes = sun8i_codec_dapm_routes,
|
||||||
|
.num_dapm_routes = ARRAY_SIZE(sun8i_codec_dapm_routes),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config sun8i_codec_regmap_config = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.val_bits = 32,
|
||||||
|
.max_register = SUN8I_DAC_MXR_SRC,
|
||||||
|
|
||||||
|
.cache_type = REGCACHE_FLAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sun8i_codec_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct resource *res_base;
|
||||||
|
struct sun8i_codec *scodec;
|
||||||
|
void __iomem *base;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
|
||||||
|
if (!scodec)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
scodec->dev = &pdev->dev;
|
||||||
|
|
||||||
|
scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
|
||||||
|
if (IS_ERR(scodec->clk_module)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to get the module clock\n");
|
||||||
|
return PTR_ERR(scodec->clk_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
scodec->clk_bus = devm_clk_get(&pdev->dev, "bus");
|
||||||
|
if (IS_ERR(scodec->clk_bus)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to get the bus clock\n");
|
||||||
|
return PTR_ERR(scodec->clk_bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
base = devm_ioremap_resource(&pdev->dev, res_base);
|
||||||
|
if (IS_ERR(base)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to map the registers\n");
|
||||||
|
return PTR_ERR(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||||
|
&sun8i_codec_regmap_config);
|
||||||
|
if (IS_ERR(scodec->regmap)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to create our regmap\n");
|
||||||
|
return PTR_ERR(scodec->regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, scodec);
|
||||||
|
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||||
|
ret = sun8i_codec_runtime_resume(&pdev->dev);
|
||||||
|
if (ret)
|
||||||
|
goto err_pm_disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_soc_register_codec(&pdev->dev, &sun8i_soc_codec,
|
||||||
|
&sun8i_codec_dai, 1);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register codec\n");
|
||||||
|
goto err_suspend;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_suspend:
|
||||||
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||||
|
sun8i_codec_runtime_suspend(&pdev->dev);
|
||||||
|
|
||||||
|
err_pm_disable:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun8i_codec_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||||
|
struct sun8i_codec *scodec = snd_soc_card_get_drvdata(card);
|
||||||
|
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||||
|
sun8i_codec_runtime_suspend(&pdev->dev);
|
||||||
|
|
||||||
|
snd_soc_unregister_codec(&pdev->dev);
|
||||||
|
clk_disable_unprepare(scodec->clk_module);
|
||||||
|
clk_disable_unprepare(scodec->clk_bus);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sun8i_codec_of_match[] = {
|
||||||
|
{ .compatible = "allwinner,sun8i-a33-codec" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sun8i_codec_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
|
||||||
|
sun8i_codec_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver sun8i_codec_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "sun8i-codec",
|
||||||
|
.of_match_table = sun8i_codec_of_match,
|
||||||
|
.pm = &sun8i_codec_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = sun8i_codec_probe,
|
||||||
|
.remove = sun8i_codec_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(sun8i_codec_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
|
||||||
|
MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:sun8i-codec");
|
Загрузка…
Ссылка в новой задаче