brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 4ec6311 Raw
321 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * IMG parallel output controller driver4 *5 * Copyright (C) 2015 Imagination Technologies Ltd.6 *7 * Author: Damien Horsley <Damien.Horsley@imgtec.com>8 */9 10#include <linux/clk.h>11#include <linux/init.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17#include <linux/reset.h>18 19#include <sound/core.h>20#include <sound/dmaengine_pcm.h>21#include <sound/initval.h>22#include <sound/pcm.h>23#include <sound/pcm_params.h>24#include <sound/soc.h>25 26#define IMG_PRL_OUT_TX_FIFO		027 28#define IMG_PRL_OUT_CTL			0x429#define IMG_PRL_OUT_CTL_CH_MASK		BIT(4)30#define IMG_PRL_OUT_CTL_PACKH_MASK	BIT(3)31#define IMG_PRL_OUT_CTL_EDGE_MASK	BIT(2)32#define IMG_PRL_OUT_CTL_ME_MASK		BIT(1)33#define IMG_PRL_OUT_CTL_SRST_MASK	BIT(0)34 35struct img_prl_out {36	void __iomem *base;37	struct clk *clk_sys;38	struct clk *clk_ref;39	struct snd_dmaengine_dai_dma_data dma_data;40	struct device *dev;41	struct reset_control *rst;42};43 44static int img_prl_out_suspend(struct device *dev)45{46	struct img_prl_out *prl = dev_get_drvdata(dev);47 48	clk_disable_unprepare(prl->clk_ref);49 50	return 0;51}52 53static int img_prl_out_resume(struct device *dev)54{55	struct img_prl_out *prl = dev_get_drvdata(dev);56	int ret;57 58	ret = clk_prepare_enable(prl->clk_ref);59	if (ret) {60		dev_err(dev, "clk_enable failed: %d\n", ret);61		return ret;62	}63 64	return 0;65}66 67static inline void img_prl_out_writel(struct img_prl_out *prl,68				u32 val, u32 reg)69{70	writel(val, prl->base + reg);71}72 73static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg)74{75	return readl(prl->base + reg);76}77 78static void img_prl_out_reset(struct img_prl_out *prl)79{80	u32 ctl;81 82	ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) &83			~IMG_PRL_OUT_CTL_ME_MASK;84 85	reset_control_assert(prl->rst);86	reset_control_deassert(prl->rst);87 88	img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL);89}90 91static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd,92			struct snd_soc_dai *dai)93{94	struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);95	u32 reg;96 97	switch (cmd) {98	case SNDRV_PCM_TRIGGER_START:99	case SNDRV_PCM_TRIGGER_RESUME:100	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:101		reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);102		reg |= IMG_PRL_OUT_CTL_ME_MASK;103		img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);104		break;105	case SNDRV_PCM_TRIGGER_STOP:106	case SNDRV_PCM_TRIGGER_SUSPEND:107	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:108		img_prl_out_reset(prl);109		break;110	default:111		return -EINVAL;112	}113 114	return 0;115}116 117static int img_prl_out_hw_params(struct snd_pcm_substream *substream,118	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)119{120	struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);121	unsigned int rate, channels;122	u32 reg, control_set = 0;123 124	rate = params_rate(params);125	channels = params_channels(params);126 127	switch (params_format(params)) {128	case SNDRV_PCM_FORMAT_S32_LE:129		control_set |= IMG_PRL_OUT_CTL_PACKH_MASK;130		break;131	case SNDRV_PCM_FORMAT_S24_LE:132		break;133	default:134		return -EINVAL;135	}136 137	if (channels != 2)138		return -EINVAL;139 140	clk_set_rate(prl->clk_ref, rate * 256);141 142	reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);143	reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set;144	img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);145 146	return 0;147}148 149static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)150{151	struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);152	u32 reg, control_set = 0;153	int ret;154 155	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {156	case SND_SOC_DAIFMT_NB_NF:157		break;158	case SND_SOC_DAIFMT_NB_IF:159		control_set |= IMG_PRL_OUT_CTL_EDGE_MASK;160		break;161	default:162		return -EINVAL;163	}164 165	ret = pm_runtime_resume_and_get(prl->dev);166	if (ret < 0)167		return ret;168 169	reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);170	reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;171	img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);172	pm_runtime_put(prl->dev);173 174	return 0;175}176 177static int img_prl_out_dai_probe(struct snd_soc_dai *dai)178{179	struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);180 181	snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL);182 183	return 0;184}185 186static const struct snd_soc_dai_ops img_prl_out_dai_ops = {187	.probe		= img_prl_out_dai_probe,188	.trigger	= img_prl_out_trigger,189	.hw_params	= img_prl_out_hw_params,190	.set_fmt	= img_prl_out_set_fmt191};192 193static struct snd_soc_dai_driver img_prl_out_dai = {194	.playback = {195		.channels_min = 2,196		.channels_max = 2,197		.rates = SNDRV_PCM_RATE_8000_192000,198		.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE199	},200	.ops = &img_prl_out_dai_ops201};202 203static const struct snd_soc_component_driver img_prl_out_component = {204	.name = "img-prl-out",205	.legacy_dai_naming = 1,206};207 208static int img_prl_out_probe(struct platform_device *pdev)209{210	struct img_prl_out *prl;211	struct resource *res;212	void __iomem *base;213	int ret;214	struct device *dev = &pdev->dev;215 216	prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL);217	if (!prl)218		return -ENOMEM;219 220	platform_set_drvdata(pdev, prl);221 222	prl->dev = &pdev->dev;223 224	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);225	if (IS_ERR(base))226		return PTR_ERR(base);227 228	prl->base = base;229 230	prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");231	if (IS_ERR(prl->rst))232		return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst),233				     "No top level reset found\n");234 235	prl->clk_sys = devm_clk_get(&pdev->dev, "sys");236	if (IS_ERR(prl->clk_sys))237		return dev_err_probe(dev, PTR_ERR(prl->clk_sys),238				     "Failed to acquire clock 'sys'\n");239 240	prl->clk_ref = devm_clk_get(&pdev->dev, "ref");241	if (IS_ERR(prl->clk_ref))242		return dev_err_probe(dev, PTR_ERR(prl->clk_ref),243				     "Failed to acquire clock 'ref'\n");244 245	ret = clk_prepare_enable(prl->clk_sys);246	if (ret)247		return ret;248 249	img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL);250	img_prl_out_reset(prl);251 252	pm_runtime_enable(&pdev->dev);253	if (!pm_runtime_enabled(&pdev->dev)) {254		ret = img_prl_out_resume(&pdev->dev);255		if (ret)256			goto err_pm_disable;257	}258 259	prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO;260	prl->dma_data.addr_width = 4;261	prl->dma_data.maxburst = 4;262 263	ret = devm_snd_soc_register_component(&pdev->dev,264			&img_prl_out_component,265			&img_prl_out_dai, 1);266	if (ret)267		goto err_suspend;268 269	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);270	if (ret)271		goto err_suspend;272 273	return 0;274 275err_suspend:276	if (!pm_runtime_status_suspended(&pdev->dev))277		img_prl_out_suspend(&pdev->dev);278err_pm_disable:279	pm_runtime_disable(&pdev->dev);280	clk_disable_unprepare(prl->clk_sys);281 282	return ret;283}284 285static void img_prl_out_dev_remove(struct platform_device *pdev)286{287	struct img_prl_out *prl = platform_get_drvdata(pdev);288 289	pm_runtime_disable(&pdev->dev);290	if (!pm_runtime_status_suspended(&pdev->dev))291		img_prl_out_suspend(&pdev->dev);292 293	clk_disable_unprepare(prl->clk_sys);294}295 296static const struct of_device_id img_prl_out_of_match[] = {297	{ .compatible = "img,parallel-out" },298	{}299};300MODULE_DEVICE_TABLE(of, img_prl_out_of_match);301 302static const struct dev_pm_ops img_prl_out_pm_ops = {303	SET_RUNTIME_PM_OPS(img_prl_out_suspend,304			   img_prl_out_resume, NULL)305};306 307static struct platform_driver img_prl_out_driver = {308	.driver = {309		.name = "img-parallel-out",310		.of_match_table = img_prl_out_of_match,311		.pm = &img_prl_out_pm_ops312	},313	.probe = img_prl_out_probe,314	.remove = img_prl_out_dev_remove315};316module_platform_driver(img_prl_out_driver);317 318MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");319MODULE_DESCRIPTION("IMG Parallel Output Driver");320MODULE_LICENSE("GPL v2");321