linux/sound/soc/img/img-i2s-out.c
<<
>>
Prefs
   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                return ret;
 352
 353        img_i2s_out_disable(i2s);
 354
 355        reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
 356        reg = (reg & ~control_mask) | control_set;
 357        img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
 358
 359        for (i = 0; i < i2s->active_channels; i++)
 360                img_i2s_out_ch_disable(i2s, i);
 361
 362        for (i = 0; i < i2s->max_i2s_chan; i++) {
 363                reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
 364                reg = (reg & ~chan_control_mask) | chan_control_set;
 365                img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
 366        }
 367
 368        for (i = 0; i < i2s->active_channels; i++)
 369                img_i2s_out_ch_enable(i2s, i);
 370
 371        img_i2s_out_enable(i2s);
 372        pm_runtime_put(i2s->dev);
 373
 374        i2s->force_clk_active = force_clk_active;
 375
 376        return 0;
 377}
 378
 379static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
 380        .trigger = img_i2s_out_trigger,
 381        .hw_params = img_i2s_out_hw_params,
 382        .set_fmt = img_i2s_out_set_fmt
 383};
 384
 385static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
 386{
 387        struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
 388
 389        snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
 390
 391        return 0;
 392}
 393
 394static const struct snd_soc_component_driver img_i2s_out_component = {
 395        .name = "img-i2s-out"
 396};
 397
 398static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
 399        struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
 400{
 401        unsigned int i2s_channels = params_channels(params) / 2;
 402        struct snd_soc_pcm_runtime *rtd = st->private_data;
 403        struct snd_dmaengine_dai_dma_data *dma_data;
 404        int ret;
 405
 406        dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), st);
 407
 408        ret = snd_hwparams_to_dma_slave_config(st, params, sc);
 409        if (ret)
 410                return ret;
 411
 412        sc->dst_addr = dma_data->addr;
 413        sc->dst_addr_width = dma_data->addr_width;
 414        sc->dst_maxburst = 4 * i2s_channels;
 415
 416        return 0;
 417}
 418
 419static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
 420        .prepare_slave_config = img_i2s_out_dma_prepare_slave_config
 421};
 422
 423static int img_i2s_out_probe(struct platform_device *pdev)
 424{
 425        struct img_i2s_out *i2s;
 426        struct resource *res;
 427        void __iomem *base;
 428        int i, ret;
 429        unsigned int max_i2s_chan_pow_2;
 430        u32 reg;
 431        struct device *dev = &pdev->dev;
 432
 433        i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
 434        if (!i2s)
 435                return -ENOMEM;
 436
 437        platform_set_drvdata(pdev, i2s);
 438
 439        i2s->dev = &pdev->dev;
 440
 441        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 442        base = devm_ioremap_resource(&pdev->dev, res);
 443        if (IS_ERR(base))
 444                return PTR_ERR(base);
 445
 446        i2s->base = base;
 447
 448        if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
 449                        &i2s->max_i2s_chan)) {
 450                dev_err(&pdev->dev, "No img,i2s-channels property\n");
 451                return -EINVAL;
 452        }
 453
 454        max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
 455
 456        i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
 457
 458        i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
 459        if (IS_ERR(i2s->rst)) {
 460                if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
 461                        dev_err(&pdev->dev, "No top level reset found\n");
 462                return PTR_ERR(i2s->rst);
 463        }
 464
 465        i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
 466        if (IS_ERR(i2s->clk_sys)) {
 467                if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
 468                        dev_err(dev, "Failed to acquire clock 'sys'\n");
 469                return PTR_ERR(i2s->clk_sys);
 470        }
 471
 472        i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
 473        if (IS_ERR(i2s->clk_ref)) {
 474                if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
 475                        dev_err(dev, "Failed to acquire clock 'ref'\n");
 476                return PTR_ERR(i2s->clk_ref);
 477        }
 478
 479        i2s->suspend_ch_ctl = devm_kcalloc(dev,
 480                i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
 481        if (!i2s->suspend_ch_ctl)
 482                return -ENOMEM;
 483
 484        pm_runtime_enable(&pdev->dev);
 485        if (!pm_runtime_enabled(&pdev->dev)) {
 486                ret = img_i2s_out_runtime_resume(&pdev->dev);
 487                if (ret)
 488                        goto err_pm_disable;
 489        }
 490        ret = pm_runtime_get_sync(&pdev->dev);
 491        if (ret < 0)
 492                goto err_suspend;
 493
 494        reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
 495        img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
 496
 497        reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
 498                IMG_I2S_OUT_CHAN_CTL_LT_MASK |
 499                IMG_I2S_OUT_CHAN_CTL_CH_MASK |
 500                (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
 501
 502        for (i = 0; i < i2s->max_i2s_chan; i++)
 503                img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
 504
 505        img_i2s_out_reset(i2s);
 506        pm_runtime_put(&pdev->dev);
 507
 508        i2s->active_channels = 1;
 509        i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
 510        i2s->dma_data.addr_width = 4;
 511        i2s->dma_data.maxburst = 4;
 512
 513        i2s->dai_driver.probe = img_i2s_out_dai_probe;
 514        i2s->dai_driver.playback.channels_min = 2;
 515        i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
 516        i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
 517        i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
 518        i2s->dai_driver.ops = &img_i2s_out_dai_ops;
 519
 520        ret = devm_snd_soc_register_component(&pdev->dev,
 521                        &img_i2s_out_component, &i2s->dai_driver, 1);
 522        if (ret)
 523                goto err_suspend;
 524
 525        ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
 526                        &img_i2s_out_dma_config, 0);
 527        if (ret)
 528                goto err_suspend;
 529
 530        return 0;
 531
 532err_suspend:
 533        if (!pm_runtime_status_suspended(&pdev->dev))
 534                img_i2s_out_runtime_suspend(&pdev->dev);
 535err_pm_disable:
 536        pm_runtime_disable(&pdev->dev);
 537
 538        return ret;
 539}
 540
 541static int img_i2s_out_dev_remove(struct platform_device *pdev)
 542{
 543        pm_runtime_disable(&pdev->dev);
 544        if (!pm_runtime_status_suspended(&pdev->dev))
 545                img_i2s_out_runtime_suspend(&pdev->dev);
 546
 547        return 0;
 548}
 549
 550#ifdef CONFIG_PM_SLEEP
 551static int img_i2s_out_suspend(struct device *dev)
 552{
 553        struct img_i2s_out *i2s = dev_get_drvdata(dev);
 554        int i, ret;
 555        u32 reg;
 556
 557        if (pm_runtime_status_suspended(dev)) {
 558                ret = img_i2s_out_runtime_resume(dev);
 559                if (ret)
 560                        return ret;
 561        }
 562
 563        for (i = 0; i < i2s->max_i2s_chan; i++) {
 564                reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
 565                i2s->suspend_ch_ctl[i] = reg;
 566        }
 567
 568        i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
 569
 570        img_i2s_out_runtime_suspend(dev);
 571
 572        return 0;
 573}
 574
 575static int img_i2s_out_resume(struct device *dev)
 576{
 577        struct img_i2s_out *i2s = dev_get_drvdata(dev);
 578        int i, ret;
 579        u32 reg;
 580
 581        ret = img_i2s_out_runtime_resume(dev);
 582        if (ret)
 583                return ret;
 584
 585        for (i = 0; i < i2s->max_i2s_chan; i++) {
 586                reg = i2s->suspend_ch_ctl[i];
 587                img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
 588        }
 589
 590        img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL);
 591
 592        if (pm_runtime_status_suspended(dev))
 593                img_i2s_out_runtime_suspend(dev);
 594
 595        return 0;
 596}
 597#endif
 598
 599static const struct of_device_id img_i2s_out_of_match[] = {
 600        { .compatible = "img,i2s-out" },
 601        {}
 602};
 603MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
 604
 605static const struct dev_pm_ops img_i2s_out_pm_ops = {
 606        SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend,
 607                           img_i2s_out_runtime_resume, NULL)
 608        SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume)
 609};
 610
 611static struct platform_driver img_i2s_out_driver = {
 612        .driver = {
 613                .name = "img-i2s-out",
 614                .of_match_table = img_i2s_out_of_match,
 615                .pm = &img_i2s_out_pm_ops
 616        },
 617        .probe = img_i2s_out_probe,
 618        .remove = img_i2s_out_dev_remove
 619};
 620module_platform_driver(img_i2s_out_driver);
 621
 622MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
 623MODULE_DESCRIPTION("IMG I2S Output Driver");
 624MODULE_LICENSE("GPL v2");
 625