brintos

brintos / linux-shallow public Read only

0
0
Text · 15.7 KiB · cae9110 Raw
719 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip PDM ALSA SoC Digital Audio Interface(DAI)  driver4 *5 * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd6 */7 8#include <linux/module.h>9#include <linux/clk.h>10#include <linux/of.h>11#include <linux/pm_runtime.h>12#include <linux/rational.h>13#include <linux/regmap.h>14#include <linux/reset.h>15#include <sound/dmaengine_pcm.h>16#include <sound/pcm_params.h>17 18#include "rockchip_pdm.h"19 20#define PDM_DMA_BURST_SIZE	(8) /* size * width: 8*4 = 32 bytes */21#define PDM_SIGNOFF_CLK_RATE	(100000000)22#define PDM_PATH_MAX		(4)23 24enum rk_pdm_version {25	RK_PDM_RK3229,26	RK_PDM_RK3308,27	RK_PDM_RV1126,28};29 30struct rk_pdm_dev {31	struct device *dev;32	struct clk *clk;33	struct clk *hclk;34	struct regmap *regmap;35	struct snd_dmaengine_dai_dma_data capture_dma_data;36	struct reset_control *reset;37	enum rk_pdm_version version;38};39 40struct rk_pdm_clkref {41	unsigned int sr;42	unsigned int clk;43	unsigned int clk_out;44};45 46struct rk_pdm_ds_ratio {47	unsigned int ratio;48	unsigned int sr;49};50 51static struct rk_pdm_clkref clkref[] = {52	{ 8000, 40960000, 2048000 },53	{ 11025, 56448000, 2822400 },54	{ 12000, 61440000, 3072000 },55	{ 8000, 98304000, 2048000 },56	{ 12000, 98304000, 3072000 },57};58 59static struct rk_pdm_ds_ratio ds_ratio[] = {60	{ 0, 192000 },61	{ 0, 176400 },62	{ 0, 128000 },63	{ 1, 96000 },64	{ 1, 88200 },65	{ 1, 64000 },66	{ 2, 48000 },67	{ 2, 44100 },68	{ 2, 32000 },69	{ 3, 24000 },70	{ 3, 22050 },71	{ 3, 16000 },72	{ 4, 12000 },73	{ 4, 11025 },74	{ 4, 8000 },75};76 77static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr,78				unsigned int *clk_src, unsigned int *clk_out)79{80	unsigned int i, count, clk, div, rate;81 82	clk = 0;83	if (!sr)84		return clk;85 86	count = ARRAY_SIZE(clkref);87	for (i = 0; i < count; i++) {88		if (sr % clkref[i].sr)89			continue;90		div = sr / clkref[i].sr;91		if ((div & (div - 1)) == 0) {92			*clk_out = clkref[i].clk_out;93			rate = clk_round_rate(pdm->clk, clkref[i].clk);94			if (rate != clkref[i].clk)95				continue;96			clk = clkref[i].clk;97			*clk_src = clkref[i].clk;98			break;99		}100	}101 102	if (!clk) {103		clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE);104		*clk_src = clk;105	}106	return clk;107}108 109static unsigned int get_pdm_ds_ratio(unsigned int sr)110{111	unsigned int i, count, ratio;112 113	ratio = 0;114	if (!sr)115		return ratio;116 117	count = ARRAY_SIZE(ds_ratio);118	for (i = 0; i < count; i++) {119		if (sr == ds_ratio[i].sr)120			ratio = ds_ratio[i].ratio;121	}122	return ratio;123}124 125static unsigned int get_pdm_cic_ratio(unsigned int clk)126{127	switch (clk) {128	case 4096000:129	case 5644800:130	case 6144000:131		return 0;132	case 2048000:133	case 2822400:134	case 3072000:135		return 1;136	case 1024000:137	case 1411200:138	case 1536000:139		return 2;140	default:141		return 1;142	}143}144 145static unsigned int samplerate_to_bit(unsigned int samplerate)146{147	switch (samplerate) {148	case 8000:149	case 11025:150	case 12000:151		return 0;152	case 16000:153	case 22050:154	case 24000:155		return 1;156	case 32000:157		return 2;158	case 44100:159	case 48000:160		return 3;161	case 64000:162	case 88200:163	case 96000:164		return 4;165	case 128000:166	case 176400:167	case 192000:168		return 5;169	default:170		return 1;171	}172}173 174static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)175{176	return snd_soc_dai_get_drvdata(dai);177}178 179static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)180{181	if (on) {182		regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,183				   PDM_DMA_RD_MSK, PDM_DMA_RD_EN);184		regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,185				   PDM_RX_MASK, PDM_RX_START);186	} else {187		regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,188				   PDM_DMA_RD_MSK, PDM_DMA_RD_DIS);189		regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,190				   PDM_RX_MASK | PDM_RX_CLR_MASK,191				   PDM_RX_STOP | PDM_RX_CLR_WR);192	}193}194 195static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,196				  struct snd_pcm_hw_params *params,197				  struct snd_soc_dai *dai)198{199	struct rk_pdm_dev *pdm = to_info(dai);200	unsigned int val = 0;201	unsigned int clk_rate, clk_div, samplerate;202	unsigned int clk_src, clk_out = 0;203	unsigned long m, n;204	bool change;205	int ret;206 207	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)208		return 0;209 210	samplerate = params_rate(params);211	clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out);212	if (!clk_rate)213		return -EINVAL;214 215	ret = clk_set_rate(pdm->clk, clk_src);216	if (ret)217		return -EINVAL;218 219	if (pdm->version == RK_PDM_RK3308 ||220	    pdm->version == RK_PDM_RV1126) {221		rational_best_approximation(clk_out, clk_src,222					    GENMASK(16 - 1, 0),223					    GENMASK(16 - 1, 0),224					    &m, &n);225 226		val = (m << PDM_FD_NUMERATOR_SFT) |227			(n << PDM_FD_DENOMINATOR_SFT);228		regmap_update_bits_check(pdm->regmap, PDM_CTRL1,229					 PDM_FD_NUMERATOR_MSK |230					 PDM_FD_DENOMINATOR_MSK,231					 val, &change);232		if (change) {233			reset_control_assert(pdm->reset);234			reset_control_deassert(pdm->reset);235			rockchip_pdm_rxctrl(pdm, 0);236		}237		clk_div = n / m;238		if (clk_div >= 40)239			val = PDM_CLK_FD_RATIO_40;240		else if (clk_div <= 35)241			val = PDM_CLK_FD_RATIO_35;242		else243			return -EINVAL;244		regmap_update_bits(pdm->regmap, PDM_CLK_CTRL,245				   PDM_CLK_FD_RATIO_MSK,246				   val);247	}248 249	if (pdm->version == RK_PDM_RV1126) {250		val = get_pdm_cic_ratio(clk_out);251		regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val);252		val = samplerate_to_bit(samplerate);253		regmap_update_bits(pdm->regmap, PDM_CTRL0,254				   PDM_SAMPLERATE_MSK, PDM_SAMPLERATE(val));255	} else {256		val = get_pdm_ds_ratio(samplerate);257		regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);258	}259 260	regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,261			   PDM_HPF_CF_MSK, PDM_HPF_60HZ);262	regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,263			   PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE);264	regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN);265	if (pdm->version != RK_PDM_RK3229)266		regmap_update_bits(pdm->regmap, PDM_CTRL0,267				   PDM_MODE_MSK, PDM_MODE_LJ);268 269	val = 0;270	switch (params_format(params)) {271	case SNDRV_PCM_FORMAT_S8:272		val |= PDM_VDW(8);273		break;274	case SNDRV_PCM_FORMAT_S16_LE:275		val |= PDM_VDW(16);276		break;277	case SNDRV_PCM_FORMAT_S20_3LE:278		val |= PDM_VDW(20);279		break;280	case SNDRV_PCM_FORMAT_S24_LE:281		val |= PDM_VDW(24);282		break;283	case SNDRV_PCM_FORMAT_S32_LE:284		val |= PDM_VDW(32);285		break;286	default:287		return -EINVAL;288	}289 290	switch (params_channels(params)) {291	case 8:292		val |= PDM_PATH3_EN;293		fallthrough;294	case 6:295		val |= PDM_PATH2_EN;296		fallthrough;297	case 4:298		val |= PDM_PATH1_EN;299		fallthrough;300	case 2:301		val |= PDM_PATH0_EN;302		break;303	default:304		dev_err(pdm->dev, "invalid channel: %d\n",305			params_channels(params));306		return -EINVAL;307	}308 309	regmap_update_bits(pdm->regmap, PDM_CTRL0,310			   PDM_PATH_MSK | PDM_VDW_MSK,311			   val);312	/* all channels share the single FIFO */313	regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK,314			   PDM_DMA_RDL(8 * params_channels(params)));315 316	return 0;317}318 319static int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai,320				unsigned int fmt)321{322	struct rk_pdm_dev *pdm = to_info(cpu_dai);323	unsigned int mask = 0, val = 0;324 325	mask = PDM_CKP_MSK;326	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {327	case SND_SOC_DAIFMT_NB_NF:328		val = PDM_CKP_NORMAL;329		break;330	case SND_SOC_DAIFMT_IB_NF:331		val = PDM_CKP_INVERTED;332		break;333	default:334		return -EINVAL;335	}336 337	pm_runtime_get_sync(cpu_dai->dev);338	regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val);339	pm_runtime_put(cpu_dai->dev);340 341	return 0;342}343 344static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd,345				struct snd_soc_dai *dai)346{347	struct rk_pdm_dev *pdm = to_info(dai);348	int ret = 0;349 350	switch (cmd) {351	case SNDRV_PCM_TRIGGER_START:352	case SNDRV_PCM_TRIGGER_RESUME:353	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:354		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)355			rockchip_pdm_rxctrl(pdm, 1);356		break;357	case SNDRV_PCM_TRIGGER_SUSPEND:358	case SNDRV_PCM_TRIGGER_STOP:359	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:360		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)361			rockchip_pdm_rxctrl(pdm, 0);362		break;363	default:364		ret = -EINVAL;365		break;366	}367 368	return ret;369}370 371static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)372{373	struct rk_pdm_dev *pdm = to_info(dai);374 375	snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data);376 377	return 0;378}379 380static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {381	.probe = rockchip_pdm_dai_probe,382	.set_fmt = rockchip_pdm_set_fmt,383	.trigger = rockchip_pdm_trigger,384	.hw_params = rockchip_pdm_hw_params,385};386 387#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000388#define ROCKCHIP_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \389			      SNDRV_PCM_FMTBIT_S20_3LE | \390			      SNDRV_PCM_FMTBIT_S24_LE | \391			      SNDRV_PCM_FMTBIT_S32_LE)392 393static struct snd_soc_dai_driver rockchip_pdm_dai = {394	.capture = {395		.stream_name = "Capture",396		.channels_min = 2,397		.channels_max = 8,398		.rates = ROCKCHIP_PDM_RATES,399		.formats = ROCKCHIP_PDM_FORMATS,400	},401	.ops = &rockchip_pdm_dai_ops,402	.symmetric_rate = 1,403};404 405static const struct snd_soc_component_driver rockchip_pdm_component = {406	.name = "rockchip-pdm",407	.legacy_dai_naming = 1,408};409 410static int rockchip_pdm_runtime_suspend(struct device *dev)411{412	struct rk_pdm_dev *pdm = dev_get_drvdata(dev);413 414	clk_disable_unprepare(pdm->clk);415	clk_disable_unprepare(pdm->hclk);416 417	return 0;418}419 420static int rockchip_pdm_runtime_resume(struct device *dev)421{422	struct rk_pdm_dev *pdm = dev_get_drvdata(dev);423	int ret;424 425	ret = clk_prepare_enable(pdm->clk);426	if (ret) {427		dev_err(pdm->dev, "clock enable failed %d\n", ret);428		return ret;429	}430 431	ret = clk_prepare_enable(pdm->hclk);432	if (ret) {433		clk_disable_unprepare(pdm->clk);434		dev_err(pdm->dev, "hclock enable failed %d\n", ret);435		return ret;436	}437 438	return 0;439}440 441static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)442{443	switch (reg) {444	case PDM_SYSCONFIG:445	case PDM_CTRL0:446	case PDM_CTRL1:447	case PDM_CLK_CTRL:448	case PDM_HPF_CTRL:449	case PDM_FIFO_CTRL:450	case PDM_DMA_CTRL:451	case PDM_INT_EN:452	case PDM_INT_CLR:453	case PDM_DATA_VALID:454		return true;455	default:456		return false;457	}458}459 460static bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg)461{462	switch (reg) {463	case PDM_SYSCONFIG:464	case PDM_CTRL0:465	case PDM_CTRL1:466	case PDM_CLK_CTRL:467	case PDM_HPF_CTRL:468	case PDM_FIFO_CTRL:469	case PDM_DMA_CTRL:470	case PDM_INT_EN:471	case PDM_INT_CLR:472	case PDM_INT_ST:473	case PDM_DATA_VALID:474	case PDM_RXFIFO_DATA:475	case PDM_VERSION:476		return true;477	default:478		return false;479	}480}481 482static bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg)483{484	switch (reg) {485	case PDM_SYSCONFIG:486	case PDM_FIFO_CTRL:487	case PDM_INT_CLR:488	case PDM_INT_ST:489	case PDM_RXFIFO_DATA:490		return true;491	default:492		return false;493	}494}495 496static bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg)497{498	switch (reg) {499	case PDM_RXFIFO_DATA:500		return true;501	default:502		return false;503	}504}505 506static const struct reg_default rockchip_pdm_reg_defaults[] = {507	{ PDM_CTRL0, 0x78000017 },508	{ PDM_CTRL1, 0x0bb8ea60 },509	{ PDM_CLK_CTRL, 0x0000e401 },510	{ PDM_DMA_CTRL, 0x0000001f },511};512 513static const struct regmap_config rockchip_pdm_regmap_config = {514	.reg_bits = 32,515	.reg_stride = 4,516	.val_bits = 32,517	.max_register = PDM_VERSION,518	.reg_defaults = rockchip_pdm_reg_defaults,519	.num_reg_defaults = ARRAY_SIZE(rockchip_pdm_reg_defaults),520	.writeable_reg = rockchip_pdm_wr_reg,521	.readable_reg = rockchip_pdm_rd_reg,522	.volatile_reg = rockchip_pdm_volatile_reg,523	.precious_reg = rockchip_pdm_precious_reg,524	.cache_type = REGCACHE_FLAT,525};526 527static const struct of_device_id rockchip_pdm_match[] __maybe_unused = {528	{ .compatible = "rockchip,pdm",529	  .data = (void *)RK_PDM_RK3229 },530	{ .compatible = "rockchip,px30-pdm",531	  .data = (void *)RK_PDM_RK3308 },532	{ .compatible = "rockchip,rk1808-pdm",533	  .data = (void *)RK_PDM_RK3308 },534	{ .compatible = "rockchip,rk3308-pdm",535	  .data = (void *)RK_PDM_RK3308 },536	{ .compatible = "rockchip,rk3568-pdm",537	  .data = (void *)RK_PDM_RV1126 },538	{ .compatible = "rockchip,rv1126-pdm",539	  .data = (void *)RK_PDM_RV1126 },540	{},541};542MODULE_DEVICE_TABLE(of, rockchip_pdm_match);543 544static int rockchip_pdm_path_parse(struct rk_pdm_dev *pdm, struct device_node *node)545{546	unsigned int path[PDM_PATH_MAX];547	int cnt = 0, ret = 0, i = 0, val = 0, msk = 0;548 549	cnt = of_count_phandle_with_args(node, "rockchip,path-map",550					 NULL);551	if (cnt != PDM_PATH_MAX)552		return cnt;553 554	ret = of_property_read_u32_array(node, "rockchip,path-map",555					 path, cnt);556	if (ret)557		return ret;558 559	for (i = 0; i < cnt; i++) {560		if (path[i] >= PDM_PATH_MAX)561			return -EINVAL;562		msk |= PDM_PATH_MASK(i);563		val |= PDM_PATH(i, path[i]);564	}565 566	regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, msk, val);567 568	return 0;569}570 571static int rockchip_pdm_probe(struct platform_device *pdev)572{573	struct device_node *node = pdev->dev.of_node;574	struct rk_pdm_dev *pdm;575	struct resource *res;576	void __iomem *regs;577	int ret;578 579	pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL);580	if (!pdm)581		return -ENOMEM;582 583	pdm->version = (enum rk_pdm_version)device_get_match_data(&pdev->dev);584	if (pdm->version == RK_PDM_RK3308) {585		pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m");586		if (IS_ERR(pdm->reset))587			return PTR_ERR(pdm->reset);588	}589 590	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);591	if (IS_ERR(regs))592		return PTR_ERR(regs);593 594	pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,595					    &rockchip_pdm_regmap_config);596	if (IS_ERR(pdm->regmap))597		return PTR_ERR(pdm->regmap);598 599	pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA;600	pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;601	pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE;602 603	pdm->dev = &pdev->dev;604	dev_set_drvdata(&pdev->dev, pdm);605 606	pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk");607	if (IS_ERR(pdm->clk))608		return PTR_ERR(pdm->clk);609 610	pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk");611	if (IS_ERR(pdm->hclk))612		return PTR_ERR(pdm->hclk);613 614	ret = clk_prepare_enable(pdm->hclk);615	if (ret)616		return ret;617 618	pm_runtime_enable(&pdev->dev);619	if (!pm_runtime_enabled(&pdev->dev)) {620		ret = rockchip_pdm_runtime_resume(&pdev->dev);621		if (ret)622			goto err_pm_disable;623	}624 625	ret = devm_snd_soc_register_component(&pdev->dev,626					      &rockchip_pdm_component,627					      &rockchip_pdm_dai, 1);628 629	if (ret) {630		dev_err(&pdev->dev, "could not register dai: %d\n", ret);631		goto err_suspend;632	}633 634	rockchip_pdm_rxctrl(pdm, 0);635 636	ret = rockchip_pdm_path_parse(pdm, node);637	if (ret != 0 && ret != -ENOENT)638		goto err_suspend;639 640	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);641	if (ret) {642		dev_err(&pdev->dev, "could not register pcm: %d\n", ret);643		goto err_suspend;644	}645 646	return 0;647 648err_suspend:649	if (!pm_runtime_status_suspended(&pdev->dev))650		rockchip_pdm_runtime_suspend(&pdev->dev);651err_pm_disable:652	pm_runtime_disable(&pdev->dev);653 654	clk_disable_unprepare(pdm->hclk);655 656	return ret;657}658 659static void rockchip_pdm_remove(struct platform_device *pdev)660{661	struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);662 663	pm_runtime_disable(&pdev->dev);664	if (!pm_runtime_status_suspended(&pdev->dev))665		rockchip_pdm_runtime_suspend(&pdev->dev);666 667	clk_disable_unprepare(pdm->clk);668	clk_disable_unprepare(pdm->hclk);669}670 671#ifdef CONFIG_PM_SLEEP672static int rockchip_pdm_suspend(struct device *dev)673{674	struct rk_pdm_dev *pdm = dev_get_drvdata(dev);675 676	regcache_mark_dirty(pdm->regmap);677 678	return 0;679}680 681static int rockchip_pdm_resume(struct device *dev)682{683	struct rk_pdm_dev *pdm = dev_get_drvdata(dev);684	int ret;685 686	ret = pm_runtime_resume_and_get(dev);687	if (ret < 0)688		return ret;689 690	ret = regcache_sync(pdm->regmap);691 692	pm_runtime_put(dev);693 694	return ret;695}696#endif697 698static const struct dev_pm_ops rockchip_pdm_pm_ops = {699	SET_RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend,700			   rockchip_pdm_runtime_resume, NULL)701	SET_SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume)702};703 704static struct platform_driver rockchip_pdm_driver = {705	.probe  = rockchip_pdm_probe,706	.remove = rockchip_pdm_remove,707	.driver = {708		.name = "rockchip-pdm",709		.of_match_table = of_match_ptr(rockchip_pdm_match),710		.pm = &rockchip_pdm_pm_ops,711	},712};713 714module_platform_driver(rockchip_pdm_driver);715 716MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");717MODULE_DESCRIPTION("Rockchip PDM Controller Driver");718MODULE_LICENSE("GPL v2");719