/* sound/soc/rockchip/rk_spdif.c * * ALSA SoC Audio Layer - Rockchip I2S Controller driver * * Copyright (c) 2014 Rockchip Electronics Co. Ltd. * Author: Jianqun * Copyright (c) 2015 Collabora Ltd. * Author: Sjoerd Simons * * 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. */ #include #include #include #include #include #include #include #include #include #include "rockchip_spdif.h" enum rk_spdif_type { RK_SPDIF_RK3066, RK_SPDIF_RK3188, RK_SPDIF_RK3288, RK_SPDIF_RK3366, }; #define RK3288_GRF_SOC_CON2 0x24c struct rk_spdif_dev { struct device *dev; struct clk *mclk; struct clk *hclk; struct snd_dmaengine_dai_dma_data playback_dma_data; struct regmap *regmap; }; static const struct of_device_id rk_spdif_match[] = { { .compatible = "rockchip,rk3066-spdif", .data = (void *)RK_SPDIF_RK3066 }, { .compatible = "rockchip,rk3188-spdif", .data = (void *)RK_SPDIF_RK3188 }, { .compatible = "rockchip,rk3288-spdif", .data = (void *)RK_SPDIF_RK3288 }, { .compatible = "rockchip,rk3366-spdif", .data = (void *)RK_SPDIF_RK3366 }, { .compatible = "rockchip,rk3368-spdif", .data = (void *)RK_SPDIF_RK3366 }, { .compatible = "rockchip,rk3399-spdif", .data = (void *)RK_SPDIF_RK3366 }, {}, }; MODULE_DEVICE_TABLE(of, rk_spdif_match); static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); regcache_cache_only(spdif->regmap, true); clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); return 0; } static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); int ret; ret = clk_prepare_enable(spdif->mclk); if (ret) { dev_err(spdif->dev, "mclk clock enable failed %d\n", ret); return ret; } ret = clk_prepare_enable(spdif->hclk); if (ret) { dev_err(spdif->dev, "hclk clock enable failed %d\n", ret); return ret; } regcache_cache_only(spdif->regmap, false); regcache_mark_dirty(spdif->regmap); ret = regcache_sync(spdif->regmap); if (ret) { clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); } return ret; } static int rk_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE; int srate, mclk; int ret; srate = params_rate(params); mclk = srate * 128; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val |= SPDIF_CFGR_VDW_16; break; case SNDRV_PCM_FORMAT_S20_3LE: val |= SPDIF_CFGR_VDW_20; break; case SNDRV_PCM_FORMAT_S24_LE: val |= SPDIF_CFGR_VDW_24; break; default: return -EINVAL; } /* Set clock and calculate divider */ ret = clk_set_rate(spdif->mclk, mclk); if (ret != 0) { dev_err(spdif->dev, "Failed to set module clock rate: %d\n", ret); return ret; } ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | SDPIF_CFGR_VDW_MASK, val); return ret; } static int rk_spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); int ret; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, SPDIF_DMACR_TDE_ENABLE | SPDIF_DMACR_TDL_MASK, SPDIF_DMACR_TDE_ENABLE | SPDIF_DMACR_TDL(16)); if (ret != 0) return ret; ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, SPDIF_XFER_TXS_START, SPDIF_XFER_TXS_START); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, SPDIF_DMACR_TDE_ENABLE, SPDIF_DMACR_TDE_DISABLE); if (ret != 0) return ret; ret = regmap_update_bits(spdif->regmap, SPDIF_XFER, SPDIF_XFER_TXS_START, SPDIF_XFER_TXS_STOP); break; default: ret = -EINVAL; break; } return ret; } static int rk_spdif_dai_probe(struct snd_soc_dai *dai) { struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); dai->playback_dma_data = &spdif->playback_dma_data; return 0; } static const struct snd_soc_dai_ops rk_spdif_dai_ops = { .hw_params = rk_spdif_hw_params, .trigger = rk_spdif_trigger, }; static struct snd_soc_dai_driver rk_spdif_dai = { .probe = rk_spdif_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE), }, .ops = &rk_spdif_dai_ops, }; static const struct snd_soc_component_driver rk_spdif_component = { .name = "rockchip-spdif", }; static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg) { switch (reg) { case SPDIF_CFGR: case SPDIF_DMACR: case SPDIF_INTCR: case SPDIF_XFER: case SPDIF_SMPDR: return true; default: return false; } } static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg) { switch (reg) { case SPDIF_CFGR: case SPDIF_SDBLR: case SPDIF_INTCR: case SPDIF_INTSR: case SPDIF_XFER: return true; default: return false; } } static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case SPDIF_INTSR: case SPDIF_SDBLR: return true; default: return false; } } static const struct regmap_config rk_spdif_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = SPDIF_SMPDR, .writeable_reg = rk_spdif_wr_reg, .readable_reg = rk_spdif_rd_reg, .volatile_reg = rk_spdif_volatile_reg, .cache_type = REGCACHE_FLAT, }; static int rk_spdif_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct rk_spdif_dev *spdif; const struct of_device_id *match; struct resource *res; void __iomem *regs; int ret; match = of_match_node(rk_spdif_match, np); if (match->data == (void *)RK_SPDIF_RK3288) { struct regmap *grf; grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(grf)) { dev_err(&pdev->dev, "rockchip_spdif missing 'rockchip,grf' \n"); return PTR_ERR(grf); } /* Select the 8 channel SPDIF solution on RK3288 as * the 2 channel one does not appear to work */ regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16); } spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); if (!spdif) return -ENOMEM; spdif->hclk = devm_clk_get(&pdev->dev, "hclk"); if (IS_ERR(spdif->hclk)) { dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n"); return PTR_ERR(spdif->hclk); } ret = clk_prepare_enable(spdif->hclk); if (ret) { dev_err(spdif->dev, "hclock enable failed %d\n", ret); return ret; } spdif->mclk = devm_clk_get(&pdev->dev, "mclk"); if (IS_ERR(spdif->mclk)) { dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n"); return PTR_ERR(spdif->mclk); } ret = clk_prepare_enable(spdif->mclk); if (ret) { dev_err(spdif->dev, "clock enable failed %d\n", ret); return ret; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(regs)) return PTR_ERR(regs); spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs, &rk_spdif_regmap_config); if (IS_ERR(spdif->regmap)) { dev_err(&pdev->dev, "Failed to initialise managed register map\n"); return PTR_ERR(spdif->regmap); } spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; spdif->playback_dma_data.maxburst = 4; spdif->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, spdif); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_request_idle(&pdev->dev); ret = devm_snd_soc_register_component(&pdev->dev, &rk_spdif_component, &rk_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI\n"); goto err_pm_runtime; } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); goto err_pm_runtime; } return 0; err_pm_runtime: pm_runtime_disable(&pdev->dev); return ret; } static int rk_spdif_remove(struct platform_device *pdev) { struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev); pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) rk_spdif_runtime_suspend(&pdev->dev); clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); return 0; } static const struct dev_pm_ops rk_spdif_pm_ops = { SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL) }; static struct platform_driver rk_spdif_driver = { .probe = rk_spdif_probe, .remove = rk_spdif_remove, .driver = { .name = "rockchip-spdif", .of_match_table = of_match_ptr(rk_spdif_match), .pm = &rk_spdif_pm_ops, }, }; module_platform_driver(rk_spdif_driver); MODULE_ALIAS("platform:rockchip-spdif"); MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface"); MODULE_AUTHOR("Sjoerd Simons "); MODULE_LICENSE("GPL v2"); ass='button' href='/cgit.cgi/linux/net-next.git/plain/include/dt-bindings/clock/berlin2.h?id=e7fe9491261ee07b0c7456df42c34ca6a8538804'>plain -rw-r--r--berlin2q.h695logplain -rw-r--r--clps711x-clock.h718logplain -rw-r--r--efm32-cmu.h1112logplain -rw-r--r--exynos-audss-clk.h597logplain -rw-r--r--exynos3250.h9083logplain -rw-r--r--exynos4.h8284logplain -rw-r--r--exynos4415.h9828logplain -rw-r--r--exynos5250.h4616logplain -rw-r--r--exynos5260-clk.h14876logplain -rw-r--r--exynos5410.h1689logplain -rw-r--r--exynos5420.h6857logplain -rw-r--r--exynos5433.h45372logplain -rw-r--r--exynos5440.h1141logplain -rw-r--r--exynos7-clk.h5281logplain -rw-r--r--gxbb-aoclkc.h2866logplain -rw-r--r--gxbb-clkc.h592logplain -rw-r--r--hi3516cv300-clock.h1668logplain -rw-r--r--hi3519-clock.h1328logplain -rw-r--r--hi3620-clock.h4496logplain -rw-r--r--hi6220-clock.h4508logplain -rw-r--r--hip04-clock.h1137logplain -rw-r--r--histb-clock.h2012logplain -rw-r--r--hix5hd2-clock.h2415logplain -rw-r--r--imx1-clock.h1055logplain -rw-r--r--imx21-clock.h2461logplain -rw-r--r--imx27-clock.h3494logplain -rw-r--r--imx5-clock.h7212logplain -rw-r--r--imx6qdl-clock.h9593logplain -rw-r--r--imx6sl-clock.h5849logplain -rw-r--r--imx6sx-clock.h9099logplain -rw-r--r--imx6ul-clock.h8203logplain -rw-r--r--imx7d-clock.h15974logplain -rw-r--r--jz4740-cgu.h1028logplain -rw-r--r--jz4780-cgu.h2470logplain -rw-r--r--lpc18xx-ccu.h2134logplain -rw-r--r--lpc18xx-cgu.h1142logplain -rw-r--r--lpc32xx-clock.h1633logplain -rw-r--r--lsi,axm5516-clks.h974logplain -rw-r--r--marvell,mmp2.h2022logplain -rw-r--r--marvell,pxa168.h1654logplain -rw-r--r--marvell,pxa1928.h1535logplain -rw-r--r--marvell,pxa910.h1598logplain -rw-r--r--maxim,max77620.h632logplain -rw-r--r--maxim,max77686.h648logplain -rw-r--r--maxim,max77802.h630logplain -rw-r--r--meson8b-clkc.h523logplain -rw-r--r--microchip,pic32-clock.h1150logplain -rw-r--r--mpc512x-clock.h2236logplain -rw-r--r--mt2701-clk.h13832logplain -rw-r--r--mt8135-clk.h5641logplain -rw-r--r--mt8173-clk.h9293logplain -rw-r--r--oxsemi,ox810se.h1002logplain -rw-r--r--oxsemi,ox820.h1203logplain -rw-r--r--pistachio-clk.h4863logplain -rw-r--r--pxa-clock.h1715logplain -rw-r--r--qcom,gcc-apq8084.h12872logplain -rw-r--r--qcom,gcc-ipq4019.h5423logplain -rw-r--r--qcom,gcc-ipq806x.h8574logplain -rw-r--r--qcom,gcc-mdm9615.h9497logplain -rw-r--r--qcom,gcc-msm8660.h7932logplain -rw-r--r--qcom,gcc-msm8916.h6190logplain -rw-r--r--qcom,gcc-msm8960.h9342logplain -rw-r--r--qcom,gcc-msm8974.h12340logplain -rw-r--r--qcom,gcc-msm8994.h4858logplain -rw-r--r--qcom,gcc-msm8996.h12575logplain -rw-r--r--qcom,lcc-ipq806x.h899logplain -rw-r--r--qcom,lcc-mdm9615.h1701logplain -rw-r--r--qcom,lcc-msm8960.h1616logplain -rw-r--r--qcom,mmcc-apq8084.h5722logplain -rw-r--r--qcom,mmcc-msm8960.h4109logplain -rw-r--r--qcom,mmcc-msm8974.h5223logplain -rw-r--r--qcom,mmcc-msm8996.h9403logplain -rw-r--r--qcom,rpmcc.h2101logplain -rw-r--r--r7s72100-clock.h1218logplain -rw-r--r--r8a73a4-clock.h1596logplain -rw-r--r--r8a7740-clock.h1992logplain -rw-r--r--r8a7743-cpg-mssr.h1269logplain -rw-r--r--r8a7745-cpg-mssr.h1298logplain -rw-r--r--r8a7778-clock.h1855logplain -rw-r--r--r8a7779-clock.h1647logplain -rw-r--r--r8a7790-clock.h4367logplain -rw-r--r--r8a7791-clock.h4388logplain -rw-r--r--r8a7792-clock.h2562logplain -rw-r--r--r8a7793-clock.h4561logplain -rw-r--r--r8a7794-clock.h3679logplain -rw-r--r--r8a7795-cpg-mssr.h1890logplain -rw-r--r--r8a7796-cpg-mssr.h2066logplain -rw-r--r--renesas-cpg-mssr.h542logplain -rw-r--r--rk1108-cru.h6605logplain -rw-r--r--rk3036-cru.h4584logplain -rw-r--r--rk3066a-cru.h1068logplain -rw-r--r--rk3188-cru-common.h6105logplain -rw-r--r--rk3188-cru.h1435logplain