Merge "ODROID-XU3/4:Support for I2S master mode on soc. & HDMI audio output." into odroidxu3-4.9.y

This commit is contained in:
Mauro Ribeiro
2017-02-18 09:43:42 +09:00
committed by Gerrit Code Review
13 changed files with 624 additions and 93 deletions

View File

@@ -185,9 +185,10 @@
compatible = "samsung,exynos5420-audss-clock";
reg = <0x03810000 0x0C>;
#clock-cells = <1>;
clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MAU_EPLL>,
<&clock CLK_SCLK_MAUDIO0>, <&clock CLK_SCLK_MAUPCM0>;
clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
clocks = <&clock CLK_FOUT_EPLL>, <&clock CLK_FIN_PLL>,
<&clock CLK_MOUT_MAU_EPLL_USER>, <&clock CLK_SCLK_MAUDIO0>,
<&clock CLK_SCLK_MAUPCM0>;
clock-names = "fout_epll", "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
};
mfc: codec@11000000 {

View File

@@ -13,33 +13,25 @@
/ {
sound: sound {
compatible = "simple-audio-card";
simple-audio-card,name = "Odroid-XU3";
simple-audio-card,widgets =
"Headphone", "Headphone Jack",
"Speakers", "Speakers";
simple-audio-card,routing =
"Headphone Jack", "HPL",
"Headphone Jack", "HPR",
"Headphone Jack", "MICBIAS",
"IN1", "Headphone Jack",
"Speakers", "SPKL",
"Speakers", "SPKR";
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&link0_codec>;
simple-audio-card,frame-master = <&link0_codec>;
simple-audio-card,cpu {
sound-dai = <&i2s0 0>;
system-clock-frequency = <19200000>;
};
link0_codec: simple-audio-card,codec {
sound-dai = <&max98090>;
clocks = <&i2s0 CLK_I2S_CDCLK>;
};
compatible = "hardkernel,odroid-max98090";
card-name = "odroid-snd";
clocks = <&clock CLK_FOUT_EPLL>,
<&clock CLK_MOUT_EPLL>,
<&clock CLK_MOUT_MAU_EPLL>,
<&clock CLK_MOUT_MAU_EPLL_USER>,
<&clock_audss EXYNOS_MOUT_AUDSS>,
<&clock_audss EXYNOS_MOUT_I2S>,
<&clock_audss EXYNOS_DOUT_SRP>,
<&clock_audss EXYNOS_DOUT_AUD_BUS>,
<&clock CLK_MAU_EPLL>;
clock-names = "fout_epll", "mout_sclk_epll",
"mout_mau_epll","mout_mau_epll_user",
"mout_audss", "mout_i2s",
"dout_srp","dout_aud_bus",
"mau_epll_clk";
samsung,i2s-controller = <&i2s0 0>;
samsung,audio-codec = <&max98090>;
codec-dai-name = "HiFi";
};
};
@@ -50,8 +42,6 @@
reg = <0x10>;
interrupt-parent = <&gpx3>;
interrupts = <2 0>;
clocks = <&i2s0 CLK_I2S_CDCLK>;
clock-names = "mclk";
#sound-dai-cells = <0>;
};
};

View File

@@ -254,17 +254,6 @@
status = "okay";
};
&clock_audss {
assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>,
<&clock_audss EXYNOS_MOUT_I2S>,
<&clock_audss EXYNOS_DOUT_AUD_BUS>;
assigned-clock-parents = <&clock CLK_FIN_PLL>,
<&clock_audss EXYNOS_MOUT_AUDSS>;
assigned-clock-rates = <0>,
<0>,
<19200000>;
};
&cpu0 {
cpu-supply = <&buck6_reg>;
};

View File

@@ -0,0 +1,44 @@
/*
* Hardkernel Odroid XU3 Audio Codec device tree source
*
* Copyright (c) 2015 Krzysztof Kozlowski
* Copyright (c) 2014 Collabora Ltd.
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/ {
dummy_codec: spdif-transmitter {
compatible = "linux,spdif-dit";
status = "okay";
};
sound: sound {
compatible = "hardkernel,odroid-max98090";
card-name = "odroid-snd";
clocks = <&clock CLK_FOUT_EPLL>,
<&clock CLK_MOUT_EPLL>,
<&clock CLK_MOUT_MAU_EPLL>,
<&clock CLK_MOUT_MAU_EPLL_USER>,
<&clock_audss EXYNOS_MOUT_AUDSS>,
<&clock_audss EXYNOS_MOUT_I2S>,
<&clock_audss EXYNOS_DOUT_SRP>,
<&clock_audss EXYNOS_DOUT_AUD_BUS>,
<&clock CLK_MAU_EPLL>;
clock-names = "fout_epll", "mout_sclk_epll",
"mout_mau_epll","mout_mau_epll_user",
"mout_audss", "mout_i2s",
"dout_srp","dout_aud_bus",
"mau_epll_clk";
samsung,i2s-controller = <&i2s0 0>;
samsung,audio-codec = <&dummy_codec>;
codec-dai-name = "dit-hifi";
};
};
&i2s0 {
status = "okay";
};

View File

@@ -13,6 +13,7 @@
/dts-v1/;
#include "exynos5422-odroidxu3-common.dtsi"
#include "exynos5422-odroidxu4-audio.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {

View File

@@ -1470,6 +1470,7 @@ CONFIG_TOUCHSCREEN_MMS114=y
# CONFIG_TOUCHSCREEN_ZFORCE is not set
# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
CONFIG_TOUCHSCREEN_SX865X=m
# CONFIG_TOUCHSCREEN_DWAV_USB_MT is not set
CONFIG_INPUT_MISC=y
# CONFIG_INPUT_AD714X is not set
# CONFIG_INPUT_ATMEL_CAPTOUCH is not set
@@ -1598,6 +1599,7 @@ CONFIG_TCG_TIS_I2C_INFINEON=y
# CONFIG_TCG_TIS_ST33ZP24_I2C is not set
# CONFIG_TCG_TIS_ST33ZP24_SPI is not set
# CONFIG_XILLYBUS is not set
CONFIG_EXYNOS_GPIOMEM=m
#
# I2C support
@@ -2611,18 +2613,18 @@ CONFIG_SND_SOC_SAMSUNG=y
CONFIG_SND_SAMSUNG_PCM=y
# CONFIG_SND_SAMSUNG_SPDIF is not set
CONFIG_SND_SAMSUNG_I2S=y
CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994=y
# CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994 is not set
# CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF is not set
CONFIG_SND_SOC_SMDK_WM8994_PCM=y
CONFIG_SND_SOC_SNOW=y
# CONFIG_SND_SOC_SMDK_WM8994_PCM is not set
# CONFIG_SND_SOC_SNOW is not set
# CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631 is not set
CONFIG_SND_SOC_ODROID_MAX98090=y
# CONFIG_SND_SOC_XTFPGA_I2S is not set
CONFIG_SND_SOC_I2C_AND_SPI=y
#
# CODEC drivers
#
CONFIG_SND_SOC_WM_HUBS=y
# CONFIG_SND_SOC_AC97_CODEC is not set
# CONFIG_SND_SOC_ADAU1701 is not set
# CONFIG_SND_SOC_ADAU7002 is not set
@@ -2650,7 +2652,6 @@ CONFIG_SND_SOC_WM_HUBS=y
# CONFIG_SND_SOC_GTM601 is not set
# CONFIG_SND_SOC_INNO_RK3036 is not set
CONFIG_SND_SOC_MAX98090=y
CONFIG_SND_SOC_MAX98095=y
# CONFIG_SND_SOC_MAX98504 is not set
# CONFIG_SND_SOC_MAX9860 is not set
# CONFIG_SND_SOC_PCM1681 is not set
@@ -2665,7 +2666,7 @@ CONFIG_SND_SOC_MAX98095=y
# CONFIG_SND_SOC_RT5677_SPI is not set
# CONFIG_SND_SOC_SGTL5000 is not set
# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set
# CONFIG_SND_SOC_SPDIF is not set
CONFIG_SND_SOC_SPDIF=y
# CONFIG_SND_SOC_SSM2602_SPI is not set
# CONFIG_SND_SOC_SSM2602_I2C is not set
# CONFIG_SND_SOC_SSM4567 is not set
@@ -2702,7 +2703,6 @@ CONFIG_SND_SOC_MAX98095=y
# CONFIG_SND_SOC_WM8974 is not set
# CONFIG_SND_SOC_WM8978 is not set
# CONFIG_SND_SOC_WM8985 is not set
CONFIG_SND_SOC_WM8994=y
# CONFIG_SND_SOC_NAU8810 is not set
# CONFIG_SND_SOC_TPA6130A2 is not set
CONFIG_SND_SIMPLE_CARD_UTILS=y

View File

@@ -131,33 +131,41 @@ static void exynos_audss_clk_teardown(void)
/* register exynos_audss clocks */
static int exynos_audss_clk_probe(struct platform_device *pdev)
{
const char *mout_audss_p[] = {"fin_pll", "fout_epll"};
const char *mout_audss_p[] = {"fin_pll", "mout_mau_epll_user"};
const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"};
const char *sclk_pcm_p = "sclk_pcm0";
struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
const struct exynos_audss_clk_drvdata *variant;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
int i, ret = 0;
variant = of_device_get_match_data(&pdev->dev);
if (!variant)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(&pdev->dev, res);
reg_base = of_iomap(np, 0);
if (IS_ERR(reg_base)) {
dev_err(&pdev->dev, "failed to map audss registers\n");
return PTR_ERR(reg_base);
}
epll = ERR_PTR(-ENODEV);
clk_table = devm_kzalloc(&pdev->dev,
sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
GFP_KERNEL);
if (!clk_table)
return -ENOMEM;
epll = devm_clk_get(&pdev->dev, "fout_epll");
ret = clk_prepare_enable(epll);
if (ret) {
dev_err(&pdev->dev,
"failed to prepare the epll clock\n");
return ret;
}
clk_set_rate(epll, 180633600);
dev_info(&pdev->dev, "epll %ld\n", clk_get_rate(epll));
clk_put(epll);
clk_data.clks = clk_table;
clk_data.clk_num = variant->num_clks;
@@ -167,17 +175,6 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
mout_audss_p[0] = __clk_get_name(pll_ref);
if (!IS_ERR(pll_in)) {
mout_audss_p[1] = __clk_get_name(pll_in);
if (variant->enable_epll) {
epll = pll_in;
ret = clk_prepare_enable(epll);
if (ret) {
dev_err(&pdev->dev,
"failed to prepare the epll clock\n");
return ret;
}
}
}
clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
mout_audss_p, ARRAY_SIZE(mout_audss_p),
@@ -290,7 +287,17 @@ static struct platform_driver exynos_audss_clk_driver = {
.remove = exynos_audss_clk_remove,
};
module_platform_driver(exynos_audss_clk_driver);
static int __init exynos_audss_clk_init(void)
{
return platform_driver_register(&exynos_audss_clk_driver);
}
core_initcall(exynos_audss_clk_init);
static void __exit exynos_audss_clk_exit(void)
{
platform_driver_unregister(&exynos_audss_clk_driver);
}
module_exit(exynos_audss_clk_exit);
MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");

View File

@@ -454,6 +454,7 @@ PNAME(mout_maudio0_p) = {"fin_pll", "maudio_clk", "mout_sclk_dpll",
"mout_sclk_epll", "mout_sclk_rpll"};
PNAME(mout_mau_epll_clk_p) = {"mout_sclk_epll", "mout_sclk_dpll",
"mout_sclk_mpll", "mout_sclk_spll"};
PNAME(mout_mau_epll_clk_user_p) = {"dout_osc_div", "mout_mau_epll_clk"};
PNAME(mout_mclk_cdrex_p) = {"mout_bpll", "mout_mx_mspll_ccore"};
/* List of parents specific to exynos5800 */
@@ -536,8 +537,9 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore",
mout_mx_mspll_ccore_p, SRC_TOP7, 16, 2),
MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, SRC_TOP7,
20, 2),
// MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, SRC_TOP7,
// 20, 2),
MUX(0, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1),
MUX(0, "mout_epll2", mout_epll2_5800_p, SRC_TOP7, 28, 1),
@@ -616,7 +618,6 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore",
mout_group5_5800_p, SRC_TOP7, 16, 2),
MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2),
MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1),
};
@@ -703,7 +704,7 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = {
MUX(0, "mout_sclk_spll", mout_spll_p, SRC_TOP6, 8, 1),
MUX(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1),
MUX(0, "mout_sclk_rpll", mout_rpll_p, SRC_TOP6, 16, 1),
MUX(0, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1),
MUX(CLK_MOUT_EPLL, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1),
MUX(CLK_MOUT_DPLL, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1),
MUX(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1),
@@ -797,6 +798,11 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = {
MUX(0, "mout_spi0_isp", mout_group2_p, SRC_ISP, 12, 3),
MUX(0, "mout_spi1_isp", mout_group2_p, SRC_ISP, 16, 3),
MUX(0, "mout_isp_sensor", mout_group2_p, SRC_ISP, 28, 3),
MUX(CLK_MOUT_MAU_EPLL, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7,
20, 2),
MUX(CLK_MOUT_MAU_EPLL_USER, "mout_mau_epll_clk_user", mout_mau_epll_clk_user_p,
SRC_TOP9, 8, 1),
};
static const struct samsung_div_clock exynos5x_div_clks[] __initconst = {
@@ -998,8 +1004,8 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1",
SRC_MASK_TOP2, 24, 0, 0),
GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk",
SRC_MASK_TOP7, 20, 0, 0),
GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk_user",
GATE_BUS_TOP, 23, 0, 0),
/* sclk */
GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0",
@@ -1287,7 +1293,30 @@ static const struct samsung_pll_rate_table exynos5420_vpll_rates[] __initconst =
PLL_35XX_RATE(177000000, 118, 2, 3),
PLL_35XX_RATE(100000000, 200, 3, 4),
};
static struct samsung_pll_rate_table epll_24mhz_tbl[] __initdata = {
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
PLL_36XX_RATE(600000000, 100, 2, 1, 0),
PLL_36XX_RATE(400000000, 200, 3, 2, 0),
PLL_36XX_RATE(393216000, 197, 3, 2, 39846),
PLL_36XX_RATE(361267200, 301, 5, 2, 3671),
PLL_36XX_RATE(262144000, 131, 3, 2, 4719),
PLL_36XX_RATE(200000000, 200, 3, 3, 0),
PLL_36XX_RATE(196608000, 197, 3, 3, 39846),
PLL_36XX_RATE(180633600, 301, 5, 3, 3671),
PLL_36XX_RATE(131072000, 131, 3, 3, 4719),
PLL_36XX_RATE(100000000, 200, 3, 4, 0),
PLL_36XX_RATE( 98304000, 197, 3, 4, 39846),
PLL_36XX_RATE( 90316800, 301, 5, 4, 3671),
PLL_36XX_RATE( 73728000, 393, 4, 5, 14156),
PLL_36XX_RATE( 67737600, 452, 5, 5, 27263),
PLL_36XX_RATE( 65536000, 131, 3, 4, 4719),
PLL_36XX_RATE( 49152000, 197, 3, 5, 39846),
PLL_36XX_RATE( 45158400, 301, 5, 5, 3671),
PLL_36XX_RATE( 32768000, 131, 3, 5, 4719),
{ },
};
static struct samsung_pll_clock exynos5x_plls[nr_plls] __initdata = {
[apll] = PLL(pll_2550, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK,
@@ -1414,6 +1443,7 @@ static void __init exynos5x_clk_init(struct device_node *np,
exynos5x_plls[kpll].rate_table = exynos5420_pll2550x_24mhz_tbl;
exynos5x_plls[bpll].rate_table = exynos5420_pll2550x_24mhz_tbl;
exynos5x_plls[vpll].rate_table = exynos5420_vpll_rates;
exynos5x_plls[epll].rate_table = epll_24mhz_tbl;
}
samsung_clk_register_pll(ctx, exynos5x_plls, ARRAY_SIZE(exynos5x_plls),

View File

@@ -244,6 +244,7 @@ static const struct clk_ops samsung_pll35xx_clk_min_ops = {
#define PLL36XX_SDIV_SHIFT (0)
#define PLL36XX_KDIV_SHIFT (0)
#define PLL36XX_LOCK_STAT_SHIFT (29)
#define PLL36XX_PLL_ENABLE_SHIFT (31)
static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -284,7 +285,7 @@ static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate,
unsigned long parent_rate)
{
struct samsung_clk_pll *pll = to_clk_pll(hw);
u32 tmp, pll_con0, pll_con1;
u32 tmp, pll_con0, pll_con1, locktime;
const struct samsung_pll_rate_table *rate;
rate = samsung_get_pll_settings(pll, drate);
@@ -296,30 +297,24 @@ static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate,
pll_con0 = readl_relaxed(pll->con_reg);
pll_con1 = readl_relaxed(pll->con_reg + 4);
if (!(samsung_pll36xx_mpk_change(rate, pll_con0, pll_con1))) {
/* If only s change, change just s value only*/
pll_con0 &= ~(PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT);
pll_con0 |= (rate->sdiv << PLL36XX_SDIV_SHIFT);
writel_relaxed(pll_con0, pll->con_reg);
return 0;
}
/* Set PLL lock time. */
writel_relaxed(rate->pdiv * PLL36XX_LOCK_FACTOR, pll->lock_reg);
/* Change PLL PMS values */
pll_con0 &= ~((PLL36XX_MDIV_MASK << PLL36XX_MDIV_SHIFT) |
(PLL36XX_PDIV_MASK << PLL36XX_PDIV_SHIFT) |
(PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT));
pll_con0 |= (rate->mdiv << PLL36XX_MDIV_SHIFT) |
(rate->pdiv << PLL36XX_PDIV_SHIFT) |
(rate->sdiv << PLL36XX_SDIV_SHIFT);
writel_relaxed(pll_con0, pll->con_reg);
/* Change PLL PMS values */
pll_con0 |= rate->mdiv << PLL36XX_MDIV_SHIFT;
pll_con0 |= rate->pdiv << PLL36XX_PDIV_SHIFT;
pll_con0 |= rate->sdiv << PLL36XX_SDIV_SHIFT;
pll_con0 |= 1 << PLL36XX_PLL_ENABLE_SHIFT;
pll_con1 &= ~(PLL36XX_KDIV_MASK << PLL36XX_KDIV_SHIFT);
pll_con1 |= rate->kdiv << PLL36XX_KDIV_SHIFT;
/* Set PLL lock time. */
locktime = PLL36XX_LOCK_FACTOR * rate->pdiv;
writel_relaxed(locktime, pll->lock_reg);
writel_relaxed(pll_con0, pll->con_reg);
writel_relaxed(pll_con1, pll->con_reg + 4);
/* wait_lock_time */

View File

@@ -218,6 +218,10 @@
#define CLK_MOUT_BPLL 655
#define CLK_MOUT_MX_MSPLL_CCORE 656
#define CLK_MOUT_EPLL 660
#define CLK_MOUT_MAU_EPLL 661
#define CLK_MOUT_MAU_EPLL_USER 662
#define CLK_MOUT_DPLL 700
#define CLK_MOUT_SPI1 701
#define CLK_DOUT_SPI1 702

View File

@@ -229,4 +229,13 @@ config SND_SOC_ARNDALE_RT5631_ALC5631
select SND_SAMSUNG_I2S
select SND_SOC_RT5631
config SND_SOC_ODROID_MAX98090
tristate "SoC I2S Audio support for Maxim MAX98090."
depends on I2C
select SND_SAMSUNG_I2S
select SND_SOC_MAX98090
select SND_SOC_SPDIF
help
Say Y if you want to add support for SoC audio for Maxim MAX98090
endif #SND_SOC_SAMSUNG

View File

@@ -44,6 +44,7 @@ snd-soc-lowland-objs := lowland.o
snd-soc-littlemill-objs := littlemill.o
snd-soc-bells-objs := bells.o
snd-soc-arndale-rt5631-objs := arndale_rt5631.o
snd-soc-odroid-max98090-objs := odroid_max98090.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -69,3 +70,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
obj-$(CONFIG_SND_SOC_ODROID_MAX98090) += snd-soc-odroid-max98090.o

View File

@@ -0,0 +1,459 @@
/*
* odroid_max98090.c
*
* 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.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include "i2s.h"
#include "i2s-regs.h"
static int set_audio_clock_heirachy(struct device *card_dev);
static inline int clk_set_div(struct clk *c, unsigned long parent_rate, int n)
{
return clk_set_rate(c, (parent_rate / n));
}
/*
* The initial rate that EPLL will be set to. This is the smallest multiple (4)
* of the desired master clock frequency 256 * FS for FS = 44.1khz that can
* be generated on both the 5250 and 5420 SoCs.
*/
static int set_audss_pll_rate(struct device *card_dev, unsigned long rate)
{
int ret;
struct clk *fout_epll;
struct clk *dout_srp, *dout_aud_bus;
ret = set_audio_clock_heirachy(card_dev);
if (ret) {
dev_err(card_dev, "failed to set up clock hierarchy (%d)\n",
ret);
}
fout_epll = devm_clk_get(card_dev, "fout_epll");
if (IS_ERR(fout_epll)) {
dev_err(card_dev, "%s: failed to get fout_epll\n", __func__);
return PTR_ERR(fout_epll);
}
dout_srp = devm_clk_get(card_dev, "dout_srp");
if (IS_ERR(dout_srp)) {
dev_err(card_dev, "%s: failed to get dout_srp\n", __func__);
goto out1;
}
dout_aud_bus = devm_clk_get(card_dev, "dout_aud_bus");
if (IS_ERR(dout_aud_bus)) {
dev_err(card_dev, "%s: failed to get dout_aud_bus\n", __func__);
goto out2;
}
ret = clk_set_rate(fout_epll, rate);
if (ret < 0) {
dev_err(card_dev, "failed to clk_set_rate of fout_epll for audio\n");
goto out3;
}
ret = clk_set_div(dout_srp, rate, 3);
if (ret < 0) {
dev_err(card_dev, "failed to clk_set_rate of dout_srp for audio\n");
goto out3;
}
ret = clk_set_div(dout_aud_bus, (rate / 4), 3);
if (ret < 0) {
dev_err(card_dev, "failed to clk_set_rate of dout_aud_bus for audio\n");
goto out3;
}
dev_dbg(card_dev,"%s[%d] : epll=%ld\n",__func__,__LINE__,clk_get_rate(fout_epll));
dev_dbg(card_dev,"%s[%d] : dout_srp=%ld\n",__func__,__LINE__,clk_get_rate(dout_srp));
dev_dbg(card_dev,"%s[%d] : dout_bus=%ld\n",__func__,__LINE__,clk_get_rate(dout_aud_bus));
out3:
clk_put(dout_aud_bus);
out2:
clk_put(dout_srp);
out1:
clk_put(fout_epll);
return 0;
}
/* Audio clock settings are belonged to board specific part. Every
* board can set audio source clock setting which is matched with H/W
* like this function-'set_audio_clock_heirachy'.
*/
static int set_audio_clock_heirachy(struct device *card_dev)
{
struct clk *fout_epll, *mout_sclk_epll;
struct clk *mout_mau_epll, *mout_mau_epll_user;
struct clk *mout_audss, *mout_i2s;
struct clk *mau_epll;
int ret = 0;
fout_epll = devm_clk_get(card_dev, "fout_epll");
if (IS_ERR(fout_epll)) {
dev_err(card_dev, "%s: Cannot find fout_epll.\n",
__func__);
return -EINVAL;
}
clk_prepare_enable(fout_epll);
mout_sclk_epll = devm_clk_get(card_dev, "mout_sclk_epll");
if (IS_ERR(mout_sclk_epll)) {
dev_err(card_dev, "%s: Cannot find mout_sclk_epll.\n", __func__);
ret = -EINVAL;
goto out1;
}
clk_prepare_enable(mout_sclk_epll);
mout_mau_epll = devm_clk_get(card_dev, "mout_mau_epll");
if (IS_ERR(mout_mau_epll)) {
dev_err(card_dev,
"%s: Cannot find mout_mau_epll clocks.\n", __func__);
ret = -EINVAL;
goto out2;
}
clk_prepare_enable(mout_mau_epll);
mout_mau_epll_user = devm_clk_get(card_dev, "mout_mau_epll_user");
if (IS_ERR(mout_mau_epll_user)) {
dev_err(card_dev,
"%s: Cannot find mout_mau_epll_user clocks.\n", __func__);
ret = -EINVAL;
goto out3;
}
clk_prepare_enable(mout_mau_epll_user);
mout_audss = devm_clk_get(card_dev, "mout_audss");
if (IS_ERR(mout_audss)) {
dev_err(card_dev,
"%s: Cannot find mout_audss clocks.\n", __func__);
ret = -EINVAL;
goto out4;
}
clk_prepare_enable(mout_audss);
mout_i2s = devm_clk_get(card_dev, "mout_i2s");
if (IS_ERR(mout_i2s)) {
dev_err(card_dev,
"%s: Cannot find mout_i2s clocks.\n", __func__);
ret = -EINVAL;
goto out5;
}
clk_prepare_enable(mout_i2s);
mau_epll = devm_clk_get(card_dev, "mau_epll_clk");
if (IS_ERR(mau_epll)) {
dev_err(card_dev,
"%s: Cannot find mau_epll clocks.\n", __func__);
ret = -EINVAL;
goto out6;
}
clk_prepare_enable(mau_epll);
/* Set audio clock hierarchy for S/PDIF */
ret = clk_set_parent(mout_sclk_epll, fout_epll);
if (ret < 0) {
dev_err(card_dev, "Failed to set parent of epll.\n");
goto out7;
}
ret = clk_set_parent(mout_mau_epll, mout_sclk_epll);
if (ret < 0) {
dev_err(card_dev, "Failed to set parent of mout_mau_epll.\n");
goto out7;
}
ret = clk_set_parent(mout_mau_epll_user, mout_mau_epll);
if (ret < 0) {
dev_err(card_dev, "Failed to set parent of epll.\n");
goto out7;
}
ret = clk_set_parent(mout_audss, mout_mau_epll_user);
if (ret < 0) {
dev_err(card_dev, "Failed to set parent of audss.\n");
goto out7;
}
ret = clk_set_parent(mout_i2s, mout_audss);
if (ret < 0) {
dev_err(card_dev, "Failed to set parent of mout i2s.\n");
goto out7;
}
out7:
clk_put(mau_epll);
out6:
clk_put(mout_i2s);
out5:
clk_put(mout_audss);
out4:
clk_put(mout_mau_epll_user);
out3:
clk_put(mout_mau_epll);
out2:
clk_put(mout_sclk_epll);
out1:
clk_put(fout_epll);
return ret;
}
/*
* ODROID MAX98090 I2S DAI operations. (Soc master)
*/
static int odroid_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int bfs, psr, rfs, ret, div=4;
unsigned long rclk, sclk;
unsigned long pll;
struct device *card_dev = substream->pcm->card->dev;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U24_LE:
case SNDRV_PCM_FORMAT_S24_LE:
bfs = 48;
break;
case SNDRV_PCM_FORMAT_U16_LE:
case SNDRV_PCM_FORMAT_S16_LE:
bfs = 32;
break;
default:
return -EINVAL;
}
switch (params_rate(params)) {
case 16000:
case 22050:
case 24000:
case 32000:
case 44100:
case 48000:
case 88200:
case 96000:
if (bfs == 48)
rfs = 384;
else
rfs = 256;
break;
case 64000:
rfs = 384;
break;
case 8000:
case 11025:
case 12000:
if (bfs == 48)
rfs = 768;
else
rfs = 512;
break;
default:
return -EINVAL;
}
rclk = params_rate(params) * rfs;
switch (rclk) {
case 4096000:
case 5644800:
case 6144000:
case 8467200:
case 9216000:
psr = 8;
break;
case 8192000:
case 11289600:
case 12288000:
case 16934400:
case 18432000:
psr = 4;
break;
case 22579200:
case 24576000:
case 33868800:
case 36864000:
psr = 2;
break;
case 67737600:
case 73728000:
psr = 1;
break;
default:
dev_err(card_dev, "rclk = %lu is not yet supported!\n", rclk);
return -EINVAL;
}
dev_dbg(card_dev,"%s[%d]: rate=%d, bfs=%d, rfs=%d, psr=%d \n",
__func__,__LINE__, params_rate(params), bfs, rfs, psr);
sclk = rclk * psr;
pll = sclk * div;
dev_dbg(card_dev,"%s[%d]: rclk=%ld, sclk=%ld, pll=%ld\n",
__func__,__LINE__, rclk, sclk, pll);
ret = set_audss_pll_rate(card_dev, pll);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS);
ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_IN);
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK,
0, MOD_OPCLK_PCLK);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
rclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
rfs, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops odroid_ops = {
.hw_params = odroid_hw_params,
};
static struct snd_soc_dai_link odroid_dai[] = {
{ /* Primary DAI i/f */
.name = "MAX98090 AIF1",
.stream_name = "i2s0-sec",
.codec_dai_name = "HiFi",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &odroid_ops,
},
};
static struct snd_soc_card odroid_snd = {
.name = "odroid-audio",
.owner = THIS_MODULE,
.dai_link = odroid_dai,
.num_links = ARRAY_SIZE(odroid_dai),
};
static int odroid_audio_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &odroid_snd;
struct device_node *i2s_node, *codec_node;
const char *name, *codec_dai_name;
int i, ret;
if (!pdev->dev.platform_data && !pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform data supplied\n");
return -EINVAL;
}
name = of_get_property(pdev->dev.of_node, "card-name", NULL);
if (name)
card->name = name;
i2s_node = of_parse_phandle(pdev->dev.of_node,
"samsung,i2s-controller", 0);
if (!i2s_node) {
dev_err(&pdev->dev,
"Property 'i2s-controller' missing or invalid\n");
return -EINVAL;
}
codec_node = of_parse_phandle(pdev->dev.of_node,
"samsung,audio-codec", 0);
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
codec_dai_name = of_get_property(pdev->dev.of_node, "codec-dai-name", NULL);
for (i = 0; i < ARRAY_SIZE(odroid_dai); i++) {
odroid_dai[i].codec_of_node = codec_node;
odroid_dai[i].cpu_of_node = i2s_node;
odroid_dai[i].platform_of_node = i2s_node;
if (codec_dai_name)
odroid_dai[i].codec_dai_name = codec_dai_name;
}
card->dev = &pdev->dev;
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
return ret;
}
ret = set_audio_clock_heirachy(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "failed to set up clock hierarchy (%d)\n",
ret);
snd_soc_unregister_card(card);
}
return ret;
}
static int odroid_audio_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id odroid_max98090_of_match[] = {
{ .compatible = "hardkernel,odroid-max98090", },
{},
};
MODULE_DEVICE_TABLE(of, samsung_max98090_of_match);
#endif /* CONFIG_OF */
static struct platform_driver odroid_audio_driver = {
.driver = {
.name = "odroid-audio",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(odroid_max98090_of_match),
#endif
},
.probe = odroid_audio_probe,
.remove = odroid_audio_remove,
};
module_platform_driver(odroid_audio_driver);
MODULE_DESCRIPTION("ALSA SoC ODROID MAX98090");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:odroid-audio");