diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index 00c4cfa54839..f85ed2c2951c 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -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 { diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-audio.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-audio.dtsi index 9493923ec652..e46d2e7a0065 100644 --- a/arch/arm/boot/dts/exynos5422-odroidxu3-audio.dtsi +++ b/arch/arm/boot/dts/exynos5422-odroidxu3-audio.dtsi @@ -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>; }; }; diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi index e2fadd6b063e..0f541dfedba2 100755 --- a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi +++ b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi @@ -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>; }; diff --git a/arch/arm/boot/dts/exynos5422-odroidxu4-audio.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu4-audio.dtsi new file mode 100644 index 000000000000..99cf17c9606c --- /dev/null +++ b/arch/arm/boot/dts/exynos5422-odroidxu4-audio.dtsi @@ -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"; +}; diff --git a/arch/arm/boot/dts/exynos5422-odroidxu4.dts b/arch/arm/boot/dts/exynos5422-odroidxu4.dts index f7c835719b94..21bcfcacfd6c 100755 --- a/arch/arm/boot/dts/exynos5422-odroidxu4.dts +++ b/arch/arm/boot/dts/exynos5422-odroidxu4.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "exynos5422-odroidxu3-common.dtsi" +#include "exynos5422-odroidxu4-audio.dtsi" #include / { diff --git a/arch/arm/configs/odroidxu3_defconfig b/arch/arm/configs/odroidxu3_defconfig index e107d4e0f12e..55312af899bb 100644 --- a/arch/arm/configs/odroidxu3_defconfig +++ b/arch/arm/configs/odroidxu3_defconfig @@ -917,7 +917,7 @@ CONFIG_STANDALONE=y # CONFIG_PREVENT_FIRMWARE_BUILD is not set CONFIG_FW_LOADER=y CONFIG_FIRMWARE_IN_KERNEL=y -CONFIG_EXTRA_FIRMWARE="edid/1024x768.bin edid/1280x800.bin edid/1920x1080.bin edid/720x480.bin edid/800x600.bin edid/800x480.bin edid/1280x1024.bin edid/1280x720.bin edid/1440x900.bin edid/1680x1050.bin edid/1920x1080_50hz.bin edid/640x480.bin edid/720x576.bin edid/1024x600.bin" +CONFIG_EXTRA_FIRMWARE="edid/1024x768.bin edid/1280x800.bin edid/1920x1080.bin edid/720x480.bin edid/800x600.bin edid/800x480.bin edid/1280x1024.bin edid/1280x720.bin edid/1440x900.bin edid/1680x1050.bin edid/1360x768.bin edid/1366x768.bin edid/1600x1200.bin edid/1600x900.bin edid/1920x1200_30hz.bin edid/1920x1080_50hz.bin edid/640x480.bin edid/720x576.bin edid/1024x600.bin" CONFIG_EXTRA_FIRMWARE_DIR="firmware" # CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set CONFIG_WANT_DEV_COREDUMP=y @@ -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 diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index 17e68a724945..32d8b94020a2 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -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 "); MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller"); diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 202d40a987fb..4eabfa5e2865 100755 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -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), diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 9617825daabb..5800f1386452 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -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 */ diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 57ab1405b409..8d49074701e9 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -497,6 +497,24 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] = { 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80, }, }, + { + .pixel_clock = 84750000, + .conf = { + 0x01, 0xD1, 0x23, 0x11, 0x40, 0x30, 0x1E, 0xC7, + 0x84, 0xE8, 0xD4, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x48, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80, + }, + }, + { + .pixel_clock = 85860000, + .conf = { + 0x01, 0xD1, 0x24, 0x11, 0x40, 0x30, 0xD2, 0xC8, + 0x84, 0xE8, 0xD5, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x48, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80, + }, + }, { .pixel_clock = 88750000, .conf = { @@ -506,6 +524,15 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] = { 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80, }, }, + { + .pixel_clock = 89750000, + .conf = { + 0x01, 0xD1, 0x25, 0x11, 0x40, 0x30, 0x26, 0xC9, + 0x83, 0xE8, 0xE0, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x89, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, + }, + }, { .pixel_clock = 106500000, .conf = { @@ -515,6 +542,24 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] = { 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, }, }, + { + .pixel_clock = 106560000, + .conf = { + 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x78, 0x73, 0xCA, + 0x85, 0xE8, 0x0B, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, + }, + }, + { + .pixel_clock = 107800000, + .conf = { + 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8, + 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, + 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, + }, + }, { .pixel_clock = 108000000, .conf = { @@ -533,6 +578,15 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] = { 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, }, }, + { + .pixel_clock = 119000000, + .conf = { + 0x01, 0xD1, 0x31, 0x14, 0x40, 0x78, 0x41, 0xCB, + 0x89, 0xE8, 0x28, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x68, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, + }, + }, { .pixel_clock = 146250000, .conf = { @@ -551,6 +605,33 @@ static const struct hdmiphy_config hdmiphy_5420_configs[] = { 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80, }, }, + { + .pixel_clock = 162000000, + .conf = { + 0x01, 0xD1, 0x22, 0x01, 0x40, 0x30, 0xD4, 0xCD, + 0x89, 0xE8, 0xC9, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x31, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, + }, + }, + { + .pixel_clock = 164100000, + .conf = { + 0x01, 0xD1, 0x22, 0x89, 0x49, 0xB0, 0x15, 0xCE, + 0x8A, 0xF8, 0xCD, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0x2B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, + }, + }, + { + .pixel_clock = 196900000, + .conf = { + 0x01, 0xD1, 0x29, 0x1618, 0x418, 0x190, 0xF5, 0xCF, + 0x8D, 0x168, 0xF5, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x86, + 0x54, 0xFA, 0x24, 0x03, 0x00, 0x00, 0x01, 0x80, + }, + }, }; static const struct hdmiphy_config hdmiphy_5433_configs[] = { diff --git a/firmware/edid/1360x768.bin b/firmware/edid/1360x768.bin new file mode 100644 index 000000000000..a6502e171e47 Binary files /dev/null and b/firmware/edid/1360x768.bin differ diff --git a/firmware/edid/1366x768.bin b/firmware/edid/1366x768.bin new file mode 100644 index 000000000000..bfdd83fdddef Binary files /dev/null and b/firmware/edid/1366x768.bin differ diff --git a/firmware/edid/1600x1200.bin b/firmware/edid/1600x1200.bin index f2048c8260fa..369fc35a6464 100644 Binary files a/firmware/edid/1600x1200.bin and b/firmware/edid/1600x1200.bin differ diff --git a/firmware/edid/1600x900.bin b/firmware/edid/1600x900.bin new file mode 100644 index 000000000000..02023c6ae607 Binary files /dev/null and b/firmware/edid/1600x900.bin differ diff --git a/firmware/edid/1920x1200_30hz.bin b/firmware/edid/1920x1200_30hz.bin new file mode 100644 index 000000000000..b2b13db1d670 Binary files /dev/null and b/firmware/edid/1920x1200_30hz.bin differ diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 2706e8c5a382..15effce0d25f 100755 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -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 diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index f6023b46c107..173cd943ae3d 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -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 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 5d03f5ce6916..5dad5e23b9c9 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -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 diff --git a/sound/soc/samsung/odroid_max98090.c b/sound/soc/samsung/odroid_max98090.c new file mode 100644 index 000000000000..e08737cb3023 --- /dev/null +++ b/sound/soc/samsung/odroid_max98090.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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");