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