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