Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * IMG I2S output controller driver
0004  *
0005  * Copyright (C) 2015 Imagination Technologies Ltd.
0006  *
0007  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
0008  */
0009 
0010 #include <linux/clk.h>
0011 #include <linux/init.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/pm_runtime.h>
0017 #include <linux/reset.h>
0018 
0019 #include <sound/core.h>
0020 #include <sound/dmaengine_pcm.h>
0021 #include <sound/initval.h>
0022 #include <sound/pcm.h>
0023 #include <sound/pcm_params.h>
0024 #include <sound/soc.h>
0025 
0026 #define IMG_I2S_OUT_TX_FIFO         0x0
0027 
0028 #define IMG_I2S_OUT_CTL             0x4
0029 #define IMG_I2S_OUT_CTL_DATA_EN_MASK        BIT(24)
0030 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK    0xffe000
0031 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT   13
0032 #define IMG_I2S_OUT_CTL_FRM_SIZE_MASK       BIT(8)
0033 #define IMG_I2S_OUT_CTL_MASTER_MASK     BIT(6)
0034 #define IMG_I2S_OUT_CTL_CLK_MASK        BIT(5)
0035 #define IMG_I2S_OUT_CTL_CLK_EN_MASK     BIT(4)
0036 #define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK    BIT(3)
0037 #define IMG_I2S_OUT_CTL_BCLK_POL_MASK       BIT(2)
0038 #define IMG_I2S_OUT_CTL_ME_MASK         BIT(0)
0039 
0040 #define IMG_I2S_OUT_CH_CTL          0x4
0041 #define IMG_I2S_OUT_CHAN_CTL_CH_MASK        BIT(11)
0042 #define IMG_I2S_OUT_CHAN_CTL_LT_MASK        BIT(10)
0043 #define IMG_I2S_OUT_CHAN_CTL_FMT_MASK       0xf0
0044 #define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT      4
0045 #define IMG_I2S_OUT_CHAN_CTL_JUST_MASK      BIT(3)
0046 #define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK      BIT(1)
0047 #define IMG_I2S_OUT_CHAN_CTL_ME_MASK        BIT(0)
0048 
0049 #define IMG_I2S_OUT_CH_STRIDE           0x20
0050 
0051 struct img_i2s_out {
0052     void __iomem *base;
0053     struct clk *clk_sys;
0054     struct clk *clk_ref;
0055     struct snd_dmaengine_dai_dma_data dma_data;
0056     struct device *dev;
0057     unsigned int max_i2s_chan;
0058     void __iomem *channel_base;
0059     bool force_clk_active;
0060     unsigned int active_channels;
0061     struct reset_control *rst;
0062     struct snd_soc_dai_driver dai_driver;
0063     u32 suspend_ctl;
0064     u32 *suspend_ch_ctl;
0065 };
0066 
0067 static int img_i2s_out_runtime_suspend(struct device *dev)
0068 {
0069     struct img_i2s_out *i2s = dev_get_drvdata(dev);
0070 
0071     clk_disable_unprepare(i2s->clk_ref);
0072     clk_disable_unprepare(i2s->clk_sys);
0073 
0074     return 0;
0075 }
0076 
0077 static int img_i2s_out_runtime_resume(struct device *dev)
0078 {
0079     struct img_i2s_out *i2s = dev_get_drvdata(dev);
0080     int ret;
0081 
0082     ret = clk_prepare_enable(i2s->clk_sys);
0083     if (ret) {
0084         dev_err(dev, "clk_enable failed: %d\n", ret);
0085         return ret;
0086     }
0087 
0088     ret = clk_prepare_enable(i2s->clk_ref);
0089     if (ret) {
0090         dev_err(dev, "clk_enable failed: %d\n", ret);
0091         clk_disable_unprepare(i2s->clk_sys);
0092         return ret;
0093     }
0094 
0095     return 0;
0096 }
0097 
0098 static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
0099                     u32 reg)
0100 {
0101     writel(val, i2s->base + reg);
0102 }
0103 
0104 static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
0105 {
0106     return readl(i2s->base + reg);
0107 }
0108 
0109 static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
0110                     u32 chan, u32 val, u32 reg)
0111 {
0112     writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
0113 }
0114 
0115 static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
0116                     u32 reg)
0117 {
0118     return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
0119 }
0120 
0121 static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
0122 {
0123     u32 reg;
0124 
0125     reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
0126     reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
0127     img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
0128 }
0129 
0130 static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
0131 {
0132     u32 reg;
0133 
0134     reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
0135     reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
0136     img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
0137 }
0138 
0139 static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
0140 {
0141     u32 reg;
0142 
0143     reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
0144     reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
0145     img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
0146 }
0147 
0148 static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
0149 {
0150     u32 reg;
0151 
0152     reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
0153     reg |= IMG_I2S_OUT_CTL_ME_MASK;
0154     img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
0155 }
0156 
0157 static void img_i2s_out_reset(struct img_i2s_out *i2s)
0158 {
0159     int i;
0160     u32 core_ctl, chan_ctl;
0161 
0162     core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
0163             ~IMG_I2S_OUT_CTL_ME_MASK &
0164             ~IMG_I2S_OUT_CTL_DATA_EN_MASK;
0165 
0166     if (!i2s->force_clk_active)
0167         core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
0168 
0169     chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
0170             ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
0171 
0172     reset_control_assert(i2s->rst);
0173     reset_control_deassert(i2s->rst);
0174 
0175     for (i = 0; i < i2s->max_i2s_chan; i++)
0176         img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
0177 
0178     for (i = 0; i < i2s->active_channels; i++)
0179         img_i2s_out_ch_enable(i2s, i);
0180 
0181     img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
0182     img_i2s_out_enable(i2s);
0183 }
0184 
0185 static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
0186     struct snd_soc_dai *dai)
0187 {
0188     struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
0189     u32 reg;
0190 
0191     switch (cmd) {
0192     case SNDRV_PCM_TRIGGER_START:
0193     case SNDRV_PCM_TRIGGER_RESUME:
0194     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0195         reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
0196         if (!i2s->force_clk_active)
0197             reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
0198         reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
0199         img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
0200         break;
0201     case SNDRV_PCM_TRIGGER_STOP:
0202     case SNDRV_PCM_TRIGGER_SUSPEND:
0203     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0204         img_i2s_out_reset(i2s);
0205         break;
0206     default:
0207         return -EINVAL;
0208     }
0209 
0210     return 0;
0211 }
0212 
0213 static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
0214     struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
0215 {
0216     struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
0217     unsigned int channels, i2s_channels;
0218     long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
0219     int i;
0220     u32 reg, control_mask, control_set = 0;
0221     snd_pcm_format_t format;
0222 
0223     rate = params_rate(params);
0224     format = params_format(params);
0225     channels = params_channels(params);
0226     i2s_channels = channels / 2;
0227 
0228     if (format != SNDRV_PCM_FORMAT_S32_LE)
0229         return -EINVAL;
0230 
0231     if ((channels < 2) ||
0232         (channels > (i2s->max_i2s_chan * 2)) ||
0233         (channels % 2))
0234         return -EINVAL;
0235 
0236     pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
0237     if (pre_div_a < 0)
0238         return pre_div_a;
0239     pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
0240     if (pre_div_b < 0)
0241         return pre_div_b;
0242 
0243     diff_a = abs((pre_div_a / 256) - rate);
0244     diff_b = abs((pre_div_b / 384) - rate);
0245 
0246     /* If diffs are equal, use lower clock rate */
0247     if (diff_a > diff_b)
0248         clk_set_rate(i2s->clk_ref, pre_div_b);
0249     else
0250         clk_set_rate(i2s->clk_ref, pre_div_a);
0251 
0252     /*
0253      * Another driver (eg alsa machine driver) may have rejected the above
0254      * change. Get the current rate and set the register bit according to
0255      * the new minimum diff
0256      */
0257     clk_rate = clk_get_rate(i2s->clk_ref);
0258 
0259     diff_a = abs((clk_rate / 256) - rate);
0260     diff_b = abs((clk_rate / 384) - rate);
0261 
0262     if (diff_a > diff_b)
0263         control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
0264 
0265     control_set |= ((i2s_channels - 1) <<
0266                IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
0267                IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
0268 
0269     control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
0270                IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
0271 
0272     img_i2s_out_disable(i2s);
0273 
0274     reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
0275     reg = (reg & ~control_mask) | control_set;
0276     img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
0277 
0278     for (i = 0; i < i2s_channels; i++)
0279         img_i2s_out_ch_enable(i2s, i);
0280 
0281     for (; i < i2s->max_i2s_chan; i++)
0282         img_i2s_out_ch_disable(i2s, i);
0283 
0284     img_i2s_out_enable(i2s);
0285 
0286     i2s->active_channels = i2s_channels;
0287 
0288     return 0;
0289 }
0290 
0291 static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
0292 {
0293     struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
0294     int i, ret;
0295     bool force_clk_active;
0296     u32 chan_control_mask, control_mask, chan_control_set = 0;
0297     u32 reg, control_set = 0;
0298 
0299     force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
0300             SND_SOC_DAIFMT_CONT);
0301 
0302     if (force_clk_active)
0303         control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
0304 
0305     switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0306     case SND_SOC_DAIFMT_BC_FC:
0307         break;
0308     case SND_SOC_DAIFMT_BP_FP:
0309         control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
0310         break;
0311     default:
0312         return -EINVAL;
0313     }
0314 
0315     switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0316     case SND_SOC_DAIFMT_NB_NF:
0317         control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
0318         break;
0319     case SND_SOC_DAIFMT_NB_IF:
0320         control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
0321         control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
0322         break;
0323     case SND_SOC_DAIFMT_IB_NF:
0324         break;
0325     case SND_SOC_DAIFMT_IB_IF:
0326         control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
0327         break;
0328     default:
0329         return -EINVAL;
0330     }
0331 
0332     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0333     case SND_SOC_DAIFMT_I2S:
0334         chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
0335         break;
0336     case SND_SOC_DAIFMT_LEFT_J:
0337         break;
0338     default:
0339         return -EINVAL;
0340     }
0341 
0342     control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
0343                IMG_I2S_OUT_CTL_MASTER_MASK |
0344                IMG_I2S_OUT_CTL_BCLK_POL_MASK |
0345                IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
0346 
0347     chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
0348 
0349     ret = pm_runtime_resume_and_get(i2s->dev);
0350     if (ret < 0)
0351         return ret;
0352 
0353     img_i2s_out_disable(i2s);
0354 
0355     reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
0356     reg = (reg & ~control_mask) | control_set;
0357     img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
0358 
0359     for (i = 0; i < i2s->active_channels; i++)
0360         img_i2s_out_ch_disable(i2s, i);
0361 
0362     for (i = 0; i < i2s->max_i2s_chan; i++) {
0363         reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
0364         reg = (reg & ~chan_control_mask) | chan_control_set;
0365         img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
0366     }
0367 
0368     for (i = 0; i < i2s->active_channels; i++)
0369         img_i2s_out_ch_enable(i2s, i);
0370 
0371     img_i2s_out_enable(i2s);
0372     pm_runtime_put(i2s->dev);
0373 
0374     i2s->force_clk_active = force_clk_active;
0375 
0376     return 0;
0377 }
0378 
0379 static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
0380     .trigger = img_i2s_out_trigger,
0381     .hw_params = img_i2s_out_hw_params,
0382     .set_fmt = img_i2s_out_set_fmt
0383 };
0384 
0385 static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
0386 {
0387     struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
0388 
0389     snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
0390 
0391     return 0;
0392 }
0393 
0394 static const struct snd_soc_component_driver img_i2s_out_component = {
0395     .name = "img-i2s-out",
0396     .legacy_dai_naming = 1,
0397 };
0398 
0399 static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
0400     struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
0401 {
0402     unsigned int i2s_channels = params_channels(params) / 2;
0403     struct snd_soc_pcm_runtime *rtd = st->private_data;
0404     struct snd_dmaengine_dai_dma_data *dma_data;
0405     int ret;
0406 
0407     dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st);
0408 
0409     ret = snd_hwparams_to_dma_slave_config(st, params, sc);
0410     if (ret)
0411         return ret;
0412 
0413     sc->dst_addr = dma_data->addr;
0414     sc->dst_addr_width = dma_data->addr_width;
0415     sc->dst_maxburst = 4 * i2s_channels;
0416 
0417     return 0;
0418 }
0419 
0420 static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
0421     .prepare_slave_config = img_i2s_out_dma_prepare_slave_config
0422 };
0423 
0424 static int img_i2s_out_probe(struct platform_device *pdev)
0425 {
0426     struct img_i2s_out *i2s;
0427     struct resource *res;
0428     void __iomem *base;
0429     int i, ret;
0430     unsigned int max_i2s_chan_pow_2;
0431     u32 reg;
0432     struct device *dev = &pdev->dev;
0433 
0434     i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
0435     if (!i2s)
0436         return -ENOMEM;
0437 
0438     platform_set_drvdata(pdev, i2s);
0439 
0440     i2s->dev = &pdev->dev;
0441 
0442     base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0443     if (IS_ERR(base))
0444         return PTR_ERR(base);
0445 
0446     i2s->base = base;
0447 
0448     if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
0449             &i2s->max_i2s_chan)) {
0450         dev_err(&pdev->dev, "No img,i2s-channels property\n");
0451         return -EINVAL;
0452     }
0453 
0454     max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
0455 
0456     i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
0457 
0458     i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
0459     if (IS_ERR(i2s->rst))
0460         return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst),
0461                      "No top level reset found\n");
0462 
0463     i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
0464     if (IS_ERR(i2s->clk_sys))
0465         return dev_err_probe(dev, PTR_ERR(i2s->clk_sys),
0466                      "Failed to acquire clock 'sys'\n");
0467 
0468     i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
0469     if (IS_ERR(i2s->clk_ref))
0470         return dev_err_probe(dev, PTR_ERR(i2s->clk_ref),
0471                      "Failed to acquire clock 'ref'\n");
0472 
0473     i2s->suspend_ch_ctl = devm_kcalloc(dev,
0474         i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
0475     if (!i2s->suspend_ch_ctl)
0476         return -ENOMEM;
0477 
0478     pm_runtime_enable(&pdev->dev);
0479     if (!pm_runtime_enabled(&pdev->dev)) {
0480         ret = img_i2s_out_runtime_resume(&pdev->dev);
0481         if (ret)
0482             goto err_pm_disable;
0483     }
0484     ret = pm_runtime_resume_and_get(&pdev->dev);
0485     if (ret < 0)
0486         goto err_suspend;
0487 
0488     reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
0489     img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
0490 
0491     reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
0492         IMG_I2S_OUT_CHAN_CTL_LT_MASK |
0493         IMG_I2S_OUT_CHAN_CTL_CH_MASK |
0494         (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
0495 
0496     for (i = 0; i < i2s->max_i2s_chan; i++)
0497         img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
0498 
0499     img_i2s_out_reset(i2s);
0500     pm_runtime_put(&pdev->dev);
0501 
0502     i2s->active_channels = 1;
0503     i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
0504     i2s->dma_data.addr_width = 4;
0505     i2s->dma_data.maxburst = 4;
0506 
0507     i2s->dai_driver.probe = img_i2s_out_dai_probe;
0508     i2s->dai_driver.playback.channels_min = 2;
0509     i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
0510     i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
0511     i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
0512     i2s->dai_driver.ops = &img_i2s_out_dai_ops;
0513 
0514     ret = devm_snd_soc_register_component(&pdev->dev,
0515             &img_i2s_out_component, &i2s->dai_driver, 1);
0516     if (ret)
0517         goto err_suspend;
0518 
0519     ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
0520             &img_i2s_out_dma_config, 0);
0521     if (ret)
0522         goto err_suspend;
0523 
0524     return 0;
0525 
0526 err_suspend:
0527     if (!pm_runtime_status_suspended(&pdev->dev))
0528         img_i2s_out_runtime_suspend(&pdev->dev);
0529 err_pm_disable:
0530     pm_runtime_disable(&pdev->dev);
0531 
0532     return ret;
0533 }
0534 
0535 static int img_i2s_out_dev_remove(struct platform_device *pdev)
0536 {
0537     pm_runtime_disable(&pdev->dev);
0538     if (!pm_runtime_status_suspended(&pdev->dev))
0539         img_i2s_out_runtime_suspend(&pdev->dev);
0540 
0541     return 0;
0542 }
0543 
0544 #ifdef CONFIG_PM_SLEEP
0545 static int img_i2s_out_suspend(struct device *dev)
0546 {
0547     struct img_i2s_out *i2s = dev_get_drvdata(dev);
0548     int i, ret;
0549     u32 reg;
0550 
0551     if (pm_runtime_status_suspended(dev)) {
0552         ret = img_i2s_out_runtime_resume(dev);
0553         if (ret)
0554             return ret;
0555     }
0556 
0557     for (i = 0; i < i2s->max_i2s_chan; i++) {
0558         reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
0559         i2s->suspend_ch_ctl[i] = reg;
0560     }
0561 
0562     i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
0563 
0564     img_i2s_out_runtime_suspend(dev);
0565 
0566     return 0;
0567 }
0568 
0569 static int img_i2s_out_resume(struct device *dev)
0570 {
0571     struct img_i2s_out *i2s = dev_get_drvdata(dev);
0572     int i, ret;
0573     u32 reg;
0574 
0575     ret = img_i2s_out_runtime_resume(dev);
0576     if (ret)
0577         return ret;
0578 
0579     for (i = 0; i < i2s->max_i2s_chan; i++) {
0580         reg = i2s->suspend_ch_ctl[i];
0581         img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
0582     }
0583 
0584     img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL);
0585 
0586     if (pm_runtime_status_suspended(dev))
0587         img_i2s_out_runtime_suspend(dev);
0588 
0589     return 0;
0590 }
0591 #endif
0592 
0593 static const struct of_device_id img_i2s_out_of_match[] = {
0594     { .compatible = "img,i2s-out" },
0595     {}
0596 };
0597 MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
0598 
0599 static const struct dev_pm_ops img_i2s_out_pm_ops = {
0600     SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend,
0601                img_i2s_out_runtime_resume, NULL)
0602     SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume)
0603 };
0604 
0605 static struct platform_driver img_i2s_out_driver = {
0606     .driver = {
0607         .name = "img-i2s-out",
0608         .of_match_table = img_i2s_out_of_match,
0609         .pm = &img_i2s_out_pm_ops
0610     },
0611     .probe = img_i2s_out_probe,
0612     .remove = img_i2s_out_dev_remove
0613 };
0614 module_platform_driver(img_i2s_out_driver);
0615 
0616 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
0617 MODULE_DESCRIPTION("IMG I2S Output Driver");
0618 MODULE_LICENSE("GPL v2");