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-i2s-out.c (15828B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * IMG I2S 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_I2S_OUT_TX_FIFO			0x0
     27
     28#define IMG_I2S_OUT_CTL				0x4
     29#define IMG_I2S_OUT_CTL_DATA_EN_MASK		BIT(24)
     30#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK	0xffe000
     31#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT	13
     32#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK		BIT(8)
     33#define IMG_I2S_OUT_CTL_MASTER_MASK		BIT(6)
     34#define IMG_I2S_OUT_CTL_CLK_MASK		BIT(5)
     35#define IMG_I2S_OUT_CTL_CLK_EN_MASK		BIT(4)
     36#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK	BIT(3)
     37#define IMG_I2S_OUT_CTL_BCLK_POL_MASK		BIT(2)
     38#define IMG_I2S_OUT_CTL_ME_MASK			BIT(0)
     39
     40#define IMG_I2S_OUT_CH_CTL			0x4
     41#define IMG_I2S_OUT_CHAN_CTL_CH_MASK		BIT(11)
     42#define IMG_I2S_OUT_CHAN_CTL_LT_MASK		BIT(10)
     43#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK		0xf0
     44#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT		4
     45#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK		BIT(3)
     46#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK		BIT(1)
     47#define IMG_I2S_OUT_CHAN_CTL_ME_MASK		BIT(0)
     48
     49#define IMG_I2S_OUT_CH_STRIDE			0x20
     50
     51struct img_i2s_out {
     52	void __iomem *base;
     53	struct clk *clk_sys;
     54	struct clk *clk_ref;
     55	struct snd_dmaengine_dai_dma_data dma_data;
     56	struct device *dev;
     57	unsigned int max_i2s_chan;
     58	void __iomem *channel_base;
     59	bool force_clk_active;
     60	unsigned int active_channels;
     61	struct reset_control *rst;
     62	struct snd_soc_dai_driver dai_driver;
     63	u32 suspend_ctl;
     64	u32 *suspend_ch_ctl;
     65};
     66
     67static int img_i2s_out_runtime_suspend(struct device *dev)
     68{
     69	struct img_i2s_out *i2s = dev_get_drvdata(dev);
     70
     71	clk_disable_unprepare(i2s->clk_ref);
     72	clk_disable_unprepare(i2s->clk_sys);
     73
     74	return 0;
     75}
     76
     77static int img_i2s_out_runtime_resume(struct device *dev)
     78{
     79	struct img_i2s_out *i2s = dev_get_drvdata(dev);
     80	int ret;
     81
     82	ret = clk_prepare_enable(i2s->clk_sys);
     83	if (ret) {
     84		dev_err(dev, "clk_enable failed: %d\n", ret);
     85		return ret;
     86	}
     87
     88	ret = clk_prepare_enable(i2s->clk_ref);
     89	if (ret) {
     90		dev_err(dev, "clk_enable failed: %d\n", ret);
     91		clk_disable_unprepare(i2s->clk_sys);
     92		return ret;
     93	}
     94
     95	return 0;
     96}
     97
     98static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
     99					u32 reg)
    100{
    101	writel(val, i2s->base + reg);
    102}
    103
    104static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
    105{
    106	return readl(i2s->base + reg);
    107}
    108
    109static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
    110					u32 chan, u32 val, u32 reg)
    111{
    112	writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
    113}
    114
    115static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
    116					u32 reg)
    117{
    118	return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
    119}
    120
    121static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
    122{
    123	u32 reg;
    124
    125	reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
    126	reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
    127	img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
    128}
    129
    130static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
    131{
    132	u32 reg;
    133
    134	reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
    135	reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
    136	img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
    137}
    138
    139static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
    140{
    141	u32 reg;
    142
    143	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
    144	reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
    145	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
    146}
    147
    148static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
    149{
    150	u32 reg;
    151
    152	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
    153	reg |= IMG_I2S_OUT_CTL_ME_MASK;
    154	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
    155}
    156
    157static void img_i2s_out_reset(struct img_i2s_out *i2s)
    158{
    159	int i;
    160	u32 core_ctl, chan_ctl;
    161
    162	core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
    163			~IMG_I2S_OUT_CTL_ME_MASK &
    164			~IMG_I2S_OUT_CTL_DATA_EN_MASK;
    165
    166	if (!i2s->force_clk_active)
    167		core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
    168
    169	chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
    170			~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
    171
    172	reset_control_assert(i2s->rst);
    173	reset_control_deassert(i2s->rst);
    174
    175	for (i = 0; i < i2s->max_i2s_chan; i++)
    176		img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
    177
    178	for (i = 0; i < i2s->active_channels; i++)
    179		img_i2s_out_ch_enable(i2s, i);
    180
    181	img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
    182	img_i2s_out_enable(i2s);
    183}
    184
    185static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
    186	struct snd_soc_dai *dai)
    187{
    188	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
    189	u32 reg;
    190
    191	switch (cmd) {
    192	case SNDRV_PCM_TRIGGER_START:
    193	case SNDRV_PCM_TRIGGER_RESUME:
    194	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    195		reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
    196		if (!i2s->force_clk_active)
    197			reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
    198		reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
    199		img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
    200		break;
    201	case SNDRV_PCM_TRIGGER_STOP:
    202	case SNDRV_PCM_TRIGGER_SUSPEND:
    203	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    204		img_i2s_out_reset(i2s);
    205		break;
    206	default:
    207		return -EINVAL;
    208	}
    209
    210	return 0;
    211}
    212
    213static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
    214	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
    215{
    216	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
    217	unsigned int channels, i2s_channels;
    218	long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
    219	int i;
    220	u32 reg, control_mask, control_set = 0;
    221	snd_pcm_format_t format;
    222
    223	rate = params_rate(params);
    224	format = params_format(params);
    225	channels = params_channels(params);
    226	i2s_channels = channels / 2;
    227
    228	if (format != SNDRV_PCM_FORMAT_S32_LE)
    229		return -EINVAL;
    230
    231	if ((channels < 2) ||
    232	    (channels > (i2s->max_i2s_chan * 2)) ||
    233	    (channels % 2))
    234		return -EINVAL;
    235
    236	pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
    237	if (pre_div_a < 0)
    238		return pre_div_a;
    239	pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
    240	if (pre_div_b < 0)
    241		return pre_div_b;
    242
    243	diff_a = abs((pre_div_a / 256) - rate);
    244	diff_b = abs((pre_div_b / 384) - rate);
    245
    246	/* If diffs are equal, use lower clock rate */
    247	if (diff_a > diff_b)
    248		clk_set_rate(i2s->clk_ref, pre_div_b);
    249	else
    250		clk_set_rate(i2s->clk_ref, pre_div_a);
    251
    252	/*
    253	 * Another driver (eg alsa machine driver) may have rejected the above
    254	 * change. Get the current rate and set the register bit according to
    255	 * the new minimum diff
    256	 */
    257	clk_rate = clk_get_rate(i2s->clk_ref);
    258
    259	diff_a = abs((clk_rate / 256) - rate);
    260	diff_b = abs((clk_rate / 384) - rate);
    261
    262	if (diff_a > diff_b)
    263		control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
    264
    265	control_set |= ((i2s_channels - 1) <<
    266		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
    267		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
    268
    269	control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
    270		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
    271
    272	img_i2s_out_disable(i2s);
    273
    274	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
    275	reg = (reg & ~control_mask) | control_set;
    276	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
    277
    278	for (i = 0; i < i2s_channels; i++)
    279		img_i2s_out_ch_enable(i2s, i);
    280
    281	for (; i < i2s->max_i2s_chan; i++)
    282		img_i2s_out_ch_disable(i2s, i);
    283
    284	img_i2s_out_enable(i2s);
    285
    286	i2s->active_channels = i2s_channels;
    287
    288	return 0;
    289}
    290
    291static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
    292{
    293	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
    294	int i, ret;
    295	bool force_clk_active;
    296	u32 chan_control_mask, control_mask, chan_control_set = 0;
    297	u32 reg, control_set = 0;
    298
    299	force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
    300			SND_SOC_DAIFMT_CONT);
    301
    302	if (force_clk_active)
    303		control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
    304
    305	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    306	case SND_SOC_DAIFMT_CBM_CFM:
    307		break;
    308	case SND_SOC_DAIFMT_CBS_CFS:
    309		control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
    310		break;
    311	default:
    312		return -EINVAL;
    313	}
    314
    315	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    316	case SND_SOC_DAIFMT_NB_NF:
    317		control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
    318		break;
    319	case SND_SOC_DAIFMT_NB_IF:
    320		control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
    321		control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
    322		break;
    323	case SND_SOC_DAIFMT_IB_NF:
    324		break;
    325	case SND_SOC_DAIFMT_IB_IF:
    326		control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
    327		break;
    328	default:
    329		return -EINVAL;
    330	}
    331
    332	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    333	case SND_SOC_DAIFMT_I2S:
    334		chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
    335		break;
    336	case SND_SOC_DAIFMT_LEFT_J:
    337		break;
    338	default:
    339		return -EINVAL;
    340	}
    341
    342	control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
    343		       IMG_I2S_OUT_CTL_MASTER_MASK |
    344		       IMG_I2S_OUT_CTL_BCLK_POL_MASK |
    345		       IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
    346
    347	chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
    348
    349	ret = pm_runtime_get_sync(i2s->dev);
    350	if (ret < 0) {
    351		pm_runtime_put_noidle(i2s->dev);
    352		return ret;
    353	}
    354
    355	img_i2s_out_disable(i2s);
    356
    357	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
    358	reg = (reg & ~control_mask) | control_set;
    359	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
    360
    361	for (i = 0; i < i2s->active_channels; i++)
    362		img_i2s_out_ch_disable(i2s, i);
    363
    364	for (i = 0; i < i2s->max_i2s_chan; i++) {
    365		reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
    366		reg = (reg & ~chan_control_mask) | chan_control_set;
    367		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
    368	}
    369
    370	for (i = 0; i < i2s->active_channels; i++)
    371		img_i2s_out_ch_enable(i2s, i);
    372
    373	img_i2s_out_enable(i2s);
    374	pm_runtime_put(i2s->dev);
    375
    376	i2s->force_clk_active = force_clk_active;
    377
    378	return 0;
    379}
    380
    381static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
    382	.trigger = img_i2s_out_trigger,
    383	.hw_params = img_i2s_out_hw_params,
    384	.set_fmt = img_i2s_out_set_fmt
    385};
    386
    387static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
    388{
    389	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
    390
    391	snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
    392
    393	return 0;
    394}
    395
    396static const struct snd_soc_component_driver img_i2s_out_component = {
    397	.name = "img-i2s-out"
    398};
    399
    400static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
    401	struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
    402{
    403	unsigned int i2s_channels = params_channels(params) / 2;
    404	struct snd_soc_pcm_runtime *rtd = st->private_data;
    405	struct snd_dmaengine_dai_dma_data *dma_data;
    406	int ret;
    407
    408	dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st);
    409
    410	ret = snd_hwparams_to_dma_slave_config(st, params, sc);
    411	if (ret)
    412		return ret;
    413
    414	sc->dst_addr = dma_data->addr;
    415	sc->dst_addr_width = dma_data->addr_width;
    416	sc->dst_maxburst = 4 * i2s_channels;
    417
    418	return 0;
    419}
    420
    421static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
    422	.prepare_slave_config = img_i2s_out_dma_prepare_slave_config
    423};
    424
    425static int img_i2s_out_probe(struct platform_device *pdev)
    426{
    427	struct img_i2s_out *i2s;
    428	struct resource *res;
    429	void __iomem *base;
    430	int i, ret;
    431	unsigned int max_i2s_chan_pow_2;
    432	u32 reg;
    433	struct device *dev = &pdev->dev;
    434
    435	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
    436	if (!i2s)
    437		return -ENOMEM;
    438
    439	platform_set_drvdata(pdev, i2s);
    440
    441	i2s->dev = &pdev->dev;
    442
    443	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    444	if (IS_ERR(base))
    445		return PTR_ERR(base);
    446
    447	i2s->base = base;
    448
    449	if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
    450			&i2s->max_i2s_chan)) {
    451		dev_err(&pdev->dev, "No img,i2s-channels property\n");
    452		return -EINVAL;
    453	}
    454
    455	max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
    456
    457	i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
    458
    459	i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
    460	if (IS_ERR(i2s->rst))
    461		return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst),
    462				     "No top level reset found\n");
    463
    464	i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
    465	if (IS_ERR(i2s->clk_sys))
    466		return dev_err_probe(dev, PTR_ERR(i2s->clk_sys),
    467				     "Failed to acquire clock 'sys'\n");
    468
    469	i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
    470	if (IS_ERR(i2s->clk_ref))
    471		return dev_err_probe(dev, PTR_ERR(i2s->clk_ref),
    472				     "Failed to acquire clock 'ref'\n");
    473
    474	i2s->suspend_ch_ctl = devm_kcalloc(dev,
    475		i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
    476	if (!i2s->suspend_ch_ctl)
    477		return -ENOMEM;
    478
    479	pm_runtime_enable(&pdev->dev);
    480	if (!pm_runtime_enabled(&pdev->dev)) {
    481		ret = img_i2s_out_runtime_resume(&pdev->dev);
    482		if (ret)
    483			goto err_pm_disable;
    484	}
    485	ret = pm_runtime_get_sync(&pdev->dev);
    486	if (ret < 0) {
    487		pm_runtime_put_noidle(&pdev->dev);
    488		goto err_suspend;
    489	}
    490
    491	reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
    492	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
    493
    494	reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
    495		IMG_I2S_OUT_CHAN_CTL_LT_MASK |
    496		IMG_I2S_OUT_CHAN_CTL_CH_MASK |
    497		(8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
    498
    499	for (i = 0; i < i2s->max_i2s_chan; i++)
    500		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
    501
    502	img_i2s_out_reset(i2s);
    503	pm_runtime_put(&pdev->dev);
    504
    505	i2s->active_channels = 1;
    506	i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
    507	i2s->dma_data.addr_width = 4;
    508	i2s->dma_data.maxburst = 4;
    509
    510	i2s->dai_driver.probe = img_i2s_out_dai_probe;
    511	i2s->dai_driver.playback.channels_min = 2;
    512	i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
    513	i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
    514	i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
    515	i2s->dai_driver.ops = &img_i2s_out_dai_ops;
    516
    517	ret = devm_snd_soc_register_component(&pdev->dev,
    518			&img_i2s_out_component, &i2s->dai_driver, 1);
    519	if (ret)
    520		goto err_suspend;
    521
    522	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
    523			&img_i2s_out_dma_config, 0);
    524	if (ret)
    525		goto err_suspend;
    526
    527	return 0;
    528
    529err_suspend:
    530	if (!pm_runtime_status_suspended(&pdev->dev))
    531		img_i2s_out_runtime_suspend(&pdev->dev);
    532err_pm_disable:
    533	pm_runtime_disable(&pdev->dev);
    534
    535	return ret;
    536}
    537
    538static int img_i2s_out_dev_remove(struct platform_device *pdev)
    539{
    540	pm_runtime_disable(&pdev->dev);
    541	if (!pm_runtime_status_suspended(&pdev->dev))
    542		img_i2s_out_runtime_suspend(&pdev->dev);
    543
    544	return 0;
    545}
    546
    547#ifdef CONFIG_PM_SLEEP
    548static int img_i2s_out_suspend(struct device *dev)
    549{
    550	struct img_i2s_out *i2s = dev_get_drvdata(dev);
    551	int i, ret;
    552	u32 reg;
    553
    554	if (pm_runtime_status_suspended(dev)) {
    555		ret = img_i2s_out_runtime_resume(dev);
    556		if (ret)
    557			return ret;
    558	}
    559
    560	for (i = 0; i < i2s->max_i2s_chan; i++) {
    561		reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
    562		i2s->suspend_ch_ctl[i] = reg;
    563	}
    564
    565	i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
    566
    567	img_i2s_out_runtime_suspend(dev);
    568
    569	return 0;
    570}
    571
    572static int img_i2s_out_resume(struct device *dev)
    573{
    574	struct img_i2s_out *i2s = dev_get_drvdata(dev);
    575	int i, ret;
    576	u32 reg;
    577
    578	ret = img_i2s_out_runtime_resume(dev);
    579	if (ret)
    580		return ret;
    581
    582	for (i = 0; i < i2s->max_i2s_chan; i++) {
    583		reg = i2s->suspend_ch_ctl[i];
    584		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
    585	}
    586
    587	img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL);
    588
    589	if (pm_runtime_status_suspended(dev))
    590		img_i2s_out_runtime_suspend(dev);
    591
    592	return 0;
    593}
    594#endif
    595
    596static const struct of_device_id img_i2s_out_of_match[] = {
    597	{ .compatible = "img,i2s-out" },
    598	{}
    599};
    600MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
    601
    602static const struct dev_pm_ops img_i2s_out_pm_ops = {
    603	SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend,
    604			   img_i2s_out_runtime_resume, NULL)
    605	SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume)
    606};
    607
    608static struct platform_driver img_i2s_out_driver = {
    609	.driver = {
    610		.name = "img-i2s-out",
    611		.of_match_table = img_i2s_out_of_match,
    612		.pm = &img_i2s_out_pm_ops
    613	},
    614	.probe = img_i2s_out_probe,
    615	.remove = img_i2s_out_dev_remove
    616};
    617module_platform_driver(img_i2s_out_driver);
    618
    619MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
    620MODULE_DESCRIPTION("IMG I2S Output Driver");
    621MODULE_LICENSE("GPL v2");