brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · b8f2341 Raw
310 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.4 *5 * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS6 */7 8 9#include <linux/clk.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <sound/pcm.h>17#include <sound/pcm_params.h>18#include <sound/soc.h>19#include <sound/soc-dai.h>20 21#include <dt-bindings/sound/apq8016-lpass.h>22#include "lpass-lpaif-reg.h"23#include "lpass.h"24 25static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {26	[MI2S_PRIMARY] =  {27		.id = MI2S_PRIMARY,28		.name = "Primary MI2S",29		.playback = {30			.stream_name	= "Primary Playback",31			.formats	= SNDRV_PCM_FMTBIT_S16 |32						SNDRV_PCM_FMTBIT_S24 |33						SNDRV_PCM_FMTBIT_S32,34			.rates		= SNDRV_PCM_RATE_8000 |35						SNDRV_PCM_RATE_16000 |36						SNDRV_PCM_RATE_32000 |37						SNDRV_PCM_RATE_48000 |38						SNDRV_PCM_RATE_96000,39			.rate_min	= 8000,40			.rate_max	= 96000,41			.channels_min	= 1,42			.channels_max	= 8,43		},44		.ops    = &asoc_qcom_lpass_cpu_dai_ops,45	},46	[MI2S_SECONDARY] =  {47		.id = MI2S_SECONDARY,48		.name = "Secondary MI2S",49		.playback = {50			.stream_name	= "Secondary Playback",51			.formats	= SNDRV_PCM_FMTBIT_S16 |52						SNDRV_PCM_FMTBIT_S24 |53						SNDRV_PCM_FMTBIT_S32,54			.rates		= SNDRV_PCM_RATE_8000 |55						SNDRV_PCM_RATE_16000 |56						SNDRV_PCM_RATE_32000 |57						SNDRV_PCM_RATE_48000 |58						SNDRV_PCM_RATE_96000,59			.rate_min	= 8000,60			.rate_max	= 96000,61			.channels_min	= 1,62			.channels_max	= 8,63		},64		.ops    = &asoc_qcom_lpass_cpu_dai_ops,65	},66	[MI2S_TERTIARY] =  {67		.id = MI2S_TERTIARY,68		.name = "Tertiary MI2S",69		.capture = {70			.stream_name	= "Tertiary Capture",71			.formats	= SNDRV_PCM_FMTBIT_S16 |72						SNDRV_PCM_FMTBIT_S24 |73						SNDRV_PCM_FMTBIT_S32,74			.rates		= SNDRV_PCM_RATE_8000 |75						SNDRV_PCM_RATE_16000 |76						SNDRV_PCM_RATE_32000 |77						SNDRV_PCM_RATE_48000 |78						SNDRV_PCM_RATE_96000,79			.rate_min	= 8000,80			.rate_max	= 96000,81			.channels_min	= 1,82			.channels_max	= 8,83		},84		.ops    = &asoc_qcom_lpass_cpu_dai_ops,85	},86	[MI2S_QUATERNARY] =  {87		.id = MI2S_QUATERNARY,88		.name = "Quatenary MI2S",89		.playback = {90			.stream_name	= "Quatenary Playback",91			.formats	= SNDRV_PCM_FMTBIT_S16 |92						SNDRV_PCM_FMTBIT_S24 |93						SNDRV_PCM_FMTBIT_S32,94			.rates		= SNDRV_PCM_RATE_8000 |95						SNDRV_PCM_RATE_16000 |96						SNDRV_PCM_RATE_32000 |97						SNDRV_PCM_RATE_48000 |98						SNDRV_PCM_RATE_96000,99			.rate_min	= 8000,100			.rate_max	= 96000,101			.channels_min	= 1,102			.channels_max	= 8,103		},104		.capture = {105			.stream_name	= "Quatenary Capture",106			.formats	= SNDRV_PCM_FMTBIT_S16 |107						SNDRV_PCM_FMTBIT_S24 |108						SNDRV_PCM_FMTBIT_S32,109			.rates		= SNDRV_PCM_RATE_8000 |110						SNDRV_PCM_RATE_16000 |111						SNDRV_PCM_RATE_32000 |112						SNDRV_PCM_RATE_48000 |113						SNDRV_PCM_RATE_96000,114			.rate_min	= 8000,115			.rate_max	= 96000,116			.channels_min	= 1,117			.channels_max	= 8,118		},119		.ops    = &asoc_qcom_lpass_cpu_dai_ops,120	},121};122 123static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,124					   int direction, unsigned int dai_id)125{126	const struct lpass_variant *v = drvdata->variant;127	int chan = 0;128 129	if (direction == SNDRV_PCM_STREAM_PLAYBACK) {130		chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,131					v->rdma_channels);132 133		if (chan >= v->rdma_channels)134			return -EBUSY;135	} else {136		chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,137					v->wrdma_channel_start +138					v->wrdma_channels,139					v->wrdma_channel_start);140 141		if (chan >=  v->wrdma_channel_start + v->wrdma_channels)142			return -EBUSY;143	}144 145	set_bit(chan, &drvdata->dma_ch_bit_map);146 147	return chan;148}149 150static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)151{152	clear_bit(chan, &drvdata->dma_ch_bit_map);153 154	return 0;155}156 157static int apq8016_lpass_init(struct platform_device *pdev)158{159	struct lpass_data *drvdata = platform_get_drvdata(pdev);160	const struct lpass_variant *variant = drvdata->variant;161	struct device *dev = &pdev->dev;162	int ret, i;163 164 165	drvdata->clks = devm_kcalloc(dev, variant->num_clks,166				     sizeof(*drvdata->clks), GFP_KERNEL);167	if (!drvdata->clks)168		return -ENOMEM;169	drvdata->num_clks = variant->num_clks;170 171	for (i = 0; i < drvdata->num_clks; i++)172		drvdata->clks[i].id = variant->clk_name[i];173 174	ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);175	if (ret) {176		dev_err(dev, "Failed to get clocks %d\n", ret);177		return ret;178	}179 180	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);181	if (ret) {182		dev_err(dev, "apq8016 clk_enable failed\n");183		return ret;184	}185 186	drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk");187	if (IS_ERR(drvdata->ahbix_clk)) {188		dev_err(dev, "error getting ahbix-clk: %ld\n",189				PTR_ERR(drvdata->ahbix_clk));190		ret = PTR_ERR(drvdata->ahbix_clk);191		goto err_ahbix_clk;192	}193 194	ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);195	if (ret) {196		dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret);197		goto err_ahbix_clk;198	}199	dev_dbg(dev, "set ahbix_clk rate to %lu\n",200			clk_get_rate(drvdata->ahbix_clk));201 202	ret = clk_prepare_enable(drvdata->ahbix_clk);203	if (ret) {204		dev_err(dev, "error enabling ahbix_clk: %d\n", ret);205		goto err_ahbix_clk;206	}207 208	return 0;209 210err_ahbix_clk:211	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);212	return ret;213}214 215static int apq8016_lpass_exit(struct platform_device *pdev)216{217	struct lpass_data *drvdata = platform_get_drvdata(pdev);218 219	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);220	clk_disable_unprepare(drvdata->ahbix_clk);221 222	return 0;223}224 225 226static const struct lpass_variant apq8016_data = {227	.i2sctrl_reg_base	= 0x1000,228	.i2sctrl_reg_stride	= 0x1000,229	.i2s_ports		= 4,230	.irq_reg_base		= 0x6000,231	.irq_reg_stride		= 0x1000,232	.irq_ports		= 3,233	.rdma_reg_base		= 0x8400,234	.rdma_reg_stride	= 0x1000,235	.rdma_channels		= 2,236	.dmactl_audif_start	= 1,237	.wrdma_reg_base		= 0xB000,238	.wrdma_reg_stride	= 0x1000,239	.wrdma_channel_start	= 5,240	.wrdma_channels		= 2,241	.loopback		= REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000),242	.spken			= REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000),243	.spkmode		= REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000),244	.spkmono		= REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000),245	.micen			= REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000),246	.micmode		= REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000),247	.micmono		= REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000),248	.wssrc			= REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000),249	.bitwidth		= REG_FIELD_ID(0x1000, 0, 1, 4, 0x1000),250 251	.rdma_dyncclk		= REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000),252	.rdma_bursten		= REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000),253	.rdma_wpscnt		= REG_FIELD_ID(0x8400, 8, 10, 2, 0x1000),254	.rdma_intf		= REG_FIELD_ID(0x8400, 4, 7, 2, 0x1000),255	.rdma_fifowm		= REG_FIELD_ID(0x8400, 1, 3, 2, 0x1000),256	.rdma_enable		= REG_FIELD_ID(0x8400, 0, 0, 2, 0x1000),257 258	.wrdma_dyncclk		= REG_FIELD_ID(0xB000, 12, 12, 2, 0x1000),259	.wrdma_bursten		= REG_FIELD_ID(0xB000, 11, 11, 2, 0x1000),260	.wrdma_wpscnt		= REG_FIELD_ID(0xB000, 8, 10, 2, 0x1000),261	.wrdma_intf		= REG_FIELD_ID(0xB000, 4, 7, 2, 0x1000),262	.wrdma_fifowm		= REG_FIELD_ID(0xB000, 1, 3, 2, 0x1000),263	.wrdma_enable		= REG_FIELD_ID(0xB000, 0, 0, 2, 0x1000),264 265	.clk_name		= (const char*[]) {266				   "pcnoc-mport-clk",267				   "pcnoc-sway-clk",268				  },269	.num_clks		= 2,270	.dai_driver		= apq8016_lpass_cpu_dai_driver,271	.num_dai		= ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),272	.dai_osr_clk_names	= (const char *[]) {273				"mi2s-osr-clk0",274				"mi2s-osr-clk1",275				"mi2s-osr-clk2",276				"mi2s-osr-clk3",277				},278	.dai_bit_clk_names	= (const char *[]) {279				"mi2s-bit-clk0",280				"mi2s-bit-clk1",281				"mi2s-bit-clk2",282				"mi2s-bit-clk3",283				},284	.init			= apq8016_lpass_init,285	.exit			= apq8016_lpass_exit,286	.alloc_dma_channel	= apq8016_lpass_alloc_dma_channel,287	.free_dma_channel	= apq8016_lpass_free_dma_channel,288};289 290static const struct of_device_id apq8016_lpass_cpu_device_id[] __maybe_unused = {291	{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },292	{ .compatible = "qcom,apq8016-lpass-cpu", .data = &apq8016_data },293	{}294};295MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);296 297static struct platform_driver apq8016_lpass_cpu_platform_driver = {298	.driver	= {299		.name		= "apq8016-lpass-cpu",300		.of_match_table	= of_match_ptr(apq8016_lpass_cpu_device_id),301	},302	.probe	= asoc_qcom_lpass_cpu_platform_probe,303	.remove = asoc_qcom_lpass_cpu_platform_remove,304};305module_platform_driver(apq8016_lpass_cpu_platform_driver);306 307MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");308MODULE_LICENSE("GPL");309 310