cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

img-parallel-out.c (7498B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * IMG parallel output controller driver
      4 *
      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		0
     27
     28#define IMG_PRL_OUT_CTL			0x4
     29#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 const struct snd_soc_dai_ops img_prl_out_dai_ops = {
    178	.trigger = img_prl_out_trigger,
    179	.hw_params = img_prl_out_hw_params,
    180	.set_fmt = img_prl_out_set_fmt
    181};
    182
    183static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
    184{
    185	struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
    186
    187	snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL);
    188
    189	return 0;
    190}
    191
    192static struct snd_soc_dai_driver img_prl_out_dai = {
    193	.probe = img_prl_out_dai_probe,
    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_LE
    199	},
    200	.ops = &img_prl_out_dai_ops
    201};
    202
    203static const struct snd_soc_component_driver img_prl_out_component = {
    204	.name = "img-prl-out"
    205};
    206
    207static int img_prl_out_probe(struct platform_device *pdev)
    208{
    209	struct img_prl_out *prl;
    210	struct resource *res;
    211	void __iomem *base;
    212	int ret;
    213	struct device *dev = &pdev->dev;
    214
    215	prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL);
    216	if (!prl)
    217		return -ENOMEM;
    218
    219	platform_set_drvdata(pdev, prl);
    220
    221	prl->dev = &pdev->dev;
    222
    223	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    224	if (IS_ERR(base))
    225		return PTR_ERR(base);
    226
    227	prl->base = base;
    228
    229	prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
    230	if (IS_ERR(prl->rst))
    231		return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst),
    232				     "No top level reset found\n");
    233
    234	prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
    235	if (IS_ERR(prl->clk_sys))
    236		return dev_err_probe(dev, PTR_ERR(prl->clk_sys),
    237				     "Failed to acquire clock 'sys'\n");
    238
    239	prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
    240	if (IS_ERR(prl->clk_ref))
    241		return dev_err_probe(dev, PTR_ERR(prl->clk_ref),
    242				     "Failed to acquire clock 'ref'\n");
    243
    244	ret = clk_prepare_enable(prl->clk_sys);
    245	if (ret)
    246		return ret;
    247
    248	img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL);
    249	img_prl_out_reset(prl);
    250
    251	pm_runtime_enable(&pdev->dev);
    252	if (!pm_runtime_enabled(&pdev->dev)) {
    253		ret = img_prl_out_resume(&pdev->dev);
    254		if (ret)
    255			goto err_pm_disable;
    256	}
    257
    258	prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO;
    259	prl->dma_data.addr_width = 4;
    260	prl->dma_data.maxburst = 4;
    261
    262	ret = devm_snd_soc_register_component(&pdev->dev,
    263			&img_prl_out_component,
    264			&img_prl_out_dai, 1);
    265	if (ret)
    266		goto err_suspend;
    267
    268	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
    269	if (ret)
    270		goto err_suspend;
    271
    272	return 0;
    273
    274err_suspend:
    275	if (!pm_runtime_status_suspended(&pdev->dev))
    276		img_prl_out_suspend(&pdev->dev);
    277err_pm_disable:
    278	pm_runtime_disable(&pdev->dev);
    279	clk_disable_unprepare(prl->clk_sys);
    280
    281	return ret;
    282}
    283
    284static int img_prl_out_dev_remove(struct platform_device *pdev)
    285{
    286	struct img_prl_out *prl = platform_get_drvdata(pdev);
    287
    288	pm_runtime_disable(&pdev->dev);
    289	if (!pm_runtime_status_suspended(&pdev->dev))
    290		img_prl_out_suspend(&pdev->dev);
    291
    292	clk_disable_unprepare(prl->clk_sys);
    293
    294	return 0;
    295}
    296
    297static const struct of_device_id img_prl_out_of_match[] = {
    298	{ .compatible = "img,parallel-out" },
    299	{}
    300};
    301MODULE_DEVICE_TABLE(of, img_prl_out_of_match);
    302
    303static const struct dev_pm_ops img_prl_out_pm_ops = {
    304	SET_RUNTIME_PM_OPS(img_prl_out_suspend,
    305			   img_prl_out_resume, NULL)
    306};
    307
    308static struct platform_driver img_prl_out_driver = {
    309	.driver = {
    310		.name = "img-parallel-out",
    311		.of_match_table = img_prl_out_of_match,
    312		.pm = &img_prl_out_pm_ops
    313	},
    314	.probe = img_prl_out_probe,
    315	.remove = img_prl_out_dev_remove
    316};
    317module_platform_driver(img_prl_out_driver);
    318
    319MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
    320MODULE_DESCRIPTION("IMG Parallel Output Driver");
    321MODULE_LICENSE("GPL v2");