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                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                if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
 462                        dev_err(&pdev->dev, "No top level reset found\n");
 463                return PTR_ERR(i2s->rst);
 464        }
 465
 466        i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
 467        if (IS_ERR(i2s->clk_sys)) {
 468                if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
 469                        dev_err(dev, "Failed to acquire clock 'sys'\n");
 470                return PTR_ERR(i2s->clk_sys);
 471        }
 472
 473        i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
 474        if (IS_ERR(i2s->clk_ref)) {
 475                if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
 476                        dev_err(dev, "Failed to acquire clock 'ref'\n");
 477                return PTR_ERR(i2s->clk_ref);
 478        }
 479
 480        i2s->suspend_ch_ctl = devm_kcalloc(dev,
 481                i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL);
 482        if (!i2s->suspend_ch_ctl)
 483                return -ENOMEM;
 484
 485        pm_runtime_enable(&pdev->dev);
 486        if (!pm_runtime_enabled(&pdev->dev)) {
 487                ret = img_i2s_out_runtime_resume(&pdev->dev);
 488                if (ret)
 489                        goto err_pm_disable;
 490        }
 491        ret = pm_runtime_get_sync(&pdev->dev);
 492        if (ret < 0) {
 493                pm_runtime_put_noidle(&pdev->dev);
 494                goto err_suspend;
 495        }
 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