linux/sound/soc/img/img-i2s-in.c
<<
>>
Prefs
   1/*
   2 * IMG I2S input 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/reset.h>
  20
  21#include <sound/core.h>
  22#include <sound/dmaengine_pcm.h>
  23#include <sound/initval.h>
  24#include <sound/pcm.h>
  25#include <sound/pcm_params.h>
  26#include <sound/soc.h>
  27
  28#define IMG_I2S_IN_RX_FIFO                      0x0
  29
  30#define IMG_I2S_IN_CTL                          0x4
  31#define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK         0xfffffffc
  32#define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT          2
  33#define IMG_I2S_IN_CTL_16PACK_MASK              BIT(1)
  34#define IMG_I2S_IN_CTL_ME_MASK                  BIT(0)
  35
  36#define IMG_I2S_IN_CH_CTL                       0x4
  37#define IMG_I2S_IN_CH_CTL_CCDEL_MASK            0x38000
  38#define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT           15
  39#define IMG_I2S_IN_CH_CTL_FEN_MASK              BIT(14)
  40#define IMG_I2S_IN_CH_CTL_FMODE_MASK            BIT(13)
  41#define IMG_I2S_IN_CH_CTL_16PACK_MASK           BIT(12)
  42#define IMG_I2S_IN_CH_CTL_JUST_MASK             BIT(10)
  43#define IMG_I2S_IN_CH_CTL_PACKH_MASK            BIT(9)
  44#define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK        BIT(8)
  45#define IMG_I2S_IN_CH_CTL_BLKP_MASK             BIT(7)
  46#define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK       BIT(6)
  47#define IMG_I2S_IN_CH_CTL_LRD_MASK              BIT(3)
  48#define IMG_I2S_IN_CH_CTL_FW_MASK               BIT(2)
  49#define IMG_I2S_IN_CH_CTL_SW_MASK               BIT(1)
  50#define IMG_I2S_IN_CH_CTL_ME_MASK               BIT(0)
  51
  52#define IMG_I2S_IN_CH_STRIDE                    0x20
  53
  54struct img_i2s_in {
  55        void __iomem *base;
  56        struct clk *clk_sys;
  57        struct snd_dmaengine_dai_dma_data dma_data;
  58        struct device *dev;
  59        unsigned int max_i2s_chan;
  60        void __iomem *channel_base;
  61        unsigned int active_channels;
  62        struct snd_soc_dai_driver dai_driver;
  63};
  64
  65static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
  66{
  67        writel(val, i2s->base + reg);
  68}
  69
  70static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
  71{
  72        return readl(i2s->base + reg);
  73}
  74
  75static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan,
  76                                        u32 val, u32 reg)
  77{
  78        writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
  79}
  80
  81static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan,
  82                                        u32 reg)
  83{
  84        return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
  85}
  86
  87static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan)
  88{
  89        u32 reg;
  90
  91        reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
  92        reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK;
  93        img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
  94}
  95
  96static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan)
  97{
  98        u32 reg;
  99
 100        reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
 101        reg |= IMG_I2S_IN_CH_CTL_ME_MASK;
 102        img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
 103}
 104
 105static inline void img_i2s_in_disable(struct img_i2s_in *i2s)
 106{
 107        u32 reg;
 108
 109        reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
 110        reg &= ~IMG_I2S_IN_CTL_ME_MASK;
 111        img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
 112}
 113
 114static inline void img_i2s_in_enable(struct img_i2s_in *i2s)
 115{
 116        u32 reg;
 117
 118        reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
 119        reg |= IMG_I2S_IN_CTL_ME_MASK;
 120        img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
 121}
 122
 123static inline void img_i2s_in_flush(struct img_i2s_in *i2s)
 124{
 125        int i;
 126        u32 reg;
 127
 128        for (i = 0; i < i2s->active_channels; i++) {
 129                reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
 130                reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
 131                img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
 132                reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
 133                img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
 134        }
 135}
 136
 137static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd,
 138        struct snd_soc_dai *dai)
 139{
 140        struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
 141
 142        switch (cmd) {
 143        case SNDRV_PCM_TRIGGER_START:
 144        case SNDRV_PCM_TRIGGER_RESUME:
 145        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 146                img_i2s_in_enable(i2s);
 147                break;
 148
 149        case SNDRV_PCM_TRIGGER_STOP:
 150        case SNDRV_PCM_TRIGGER_SUSPEND:
 151        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 152                img_i2s_in_disable(i2s);
 153                break;
 154        default:
 155                return -EINVAL;
 156        }
 157
 158        return 0;
 159}
 160
 161static int img_i2s_in_check_rate(struct img_i2s_in *i2s,
 162                unsigned int sample_rate, unsigned int frame_size,
 163                unsigned int *bclk_filter_enable,
 164                unsigned int *bclk_filter_value)
 165{
 166        unsigned int bclk_freq, cur_freq;
 167
 168        bclk_freq = sample_rate * frame_size;
 169
 170        cur_freq = clk_get_rate(i2s->clk_sys);
 171
 172        if (cur_freq >= bclk_freq * 8) {
 173                *bclk_filter_enable = 1;
 174                *bclk_filter_value = 0;
 175        } else if (cur_freq >= bclk_freq * 7) {
 176                *bclk_filter_enable = 1;
 177                *bclk_filter_value = 1;
 178        } else if (cur_freq >= bclk_freq * 6) {
 179                *bclk_filter_enable = 0;
 180                *bclk_filter_value = 0;
 181        } else {
 182                dev_err(i2s->dev,
 183                        "Sys clock rate %u insufficient for sample rate %u\n",
 184                        cur_freq, sample_rate);
 185                return -EINVAL;
 186        }
 187
 188        return 0;
 189}
 190
 191static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
 192        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 193{
 194        struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
 195        unsigned int rate, channels, i2s_channels, frame_size;
 196        unsigned int bclk_filter_enable, bclk_filter_value;
 197        int i, ret = 0;
 198        u32 reg, control_mask, chan_control_mask;
 199        u32 control_set = 0, chan_control_set = 0;
 200        snd_pcm_format_t format;
 201
 202        rate = params_rate(params);
 203        format = params_format(params);
 204        channels = params_channels(params);
 205        i2s_channels = channels / 2;
 206
 207        switch (format) {
 208        case SNDRV_PCM_FORMAT_S32_LE:
 209                frame_size = 64;
 210                chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
 211                chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
 212                chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK;
 213                break;
 214        case SNDRV_PCM_FORMAT_S24_LE:
 215                frame_size = 64;
 216                chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
 217                chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
 218                break;
 219        case SNDRV_PCM_FORMAT_S16_LE:
 220                frame_size = 32;
 221                control_set |= IMG_I2S_IN_CTL_16PACK_MASK;
 222                chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK;
 223                break;
 224        default:
 225                return -EINVAL;
 226        }
 227
 228        if ((channels < 2) ||
 229            (channels > (i2s->max_i2s_chan * 2)) ||
 230            (channels % 2))
 231                return -EINVAL;
 232
 233        control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT);
 234
 235        ret = img_i2s_in_check_rate(i2s, rate, frame_size,
 236                        &bclk_filter_enable, &bclk_filter_value);
 237        if (ret < 0)
 238                return ret;
 239
 240        if (bclk_filter_enable)
 241                chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK;
 242
 243        if (bclk_filter_value)
 244                chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK;
 245
 246        control_mask = IMG_I2S_IN_CTL_16PACK_MASK |
 247                       IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK;
 248
 249        chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK |
 250                            IMG_I2S_IN_CH_CTL_FEN_MASK |
 251                            IMG_I2S_IN_CH_CTL_FMODE_MASK |
 252                            IMG_I2S_IN_CH_CTL_SW_MASK |
 253                            IMG_I2S_IN_CH_CTL_FW_MASK |
 254                            IMG_I2S_IN_CH_CTL_PACKH_MASK;
 255
 256        reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
 257        reg = (reg & ~control_mask) | control_set;
 258        img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
 259
 260        for (i = 0; i < i2s->active_channels; i++)
 261                img_i2s_in_ch_disable(i2s, i);
 262
 263        for (i = 0; i < i2s->max_i2s_chan; i++) {
 264                reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
 265                reg = (reg & ~chan_control_mask) | chan_control_set;
 266                img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
 267        }
 268
 269        i2s->active_channels = i2s_channels;
 270
 271        img_i2s_in_flush(i2s);
 272
 273        for (i = 0; i < i2s->active_channels; i++)
 274                img_i2s_in_ch_enable(i2s, i);
 275
 276        return 0;
 277}
 278
 279static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 280{
 281        struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
 282        int i;
 283        u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
 284        u32 reg;
 285
 286        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 287        case SND_SOC_DAIFMT_NB_NF:
 288                lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
 289                break;
 290        case SND_SOC_DAIFMT_NB_IF:
 291                break;
 292        case SND_SOC_DAIFMT_IB_NF:
 293                lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
 294                blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
 295                break;
 296        case SND_SOC_DAIFMT_IB_IF:
 297                blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
 298                break;
 299        default:
 300                return -EINVAL;
 301        }
 302
 303        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 304        case SND_SOC_DAIFMT_I2S:
 305                chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
 306                break;
 307        case SND_SOC_DAIFMT_LEFT_J:
 308                break;
 309        default:
 310                return -EINVAL;
 311        }
 312
 313        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 314        case SND_SOC_DAIFMT_CBM_CFM:
 315                break;
 316        default:
 317                return -EINVAL;
 318        }
 319
 320        chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
 321
 322        for (i = 0; i < i2s->active_channels; i++)
 323                img_i2s_in_ch_disable(i2s, i);
 324
 325        /*
 326         * BLKP and LRD must be set during separate register writes
 327         */
 328        for (i = 0; i < i2s->max_i2s_chan; i++) {
 329                reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
 330                reg = (reg & ~chan_control_mask) | chan_control_set;
 331                img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
 332                reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set;
 333                img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
 334                reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set;
 335                img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
 336        }
 337
 338        for (i = 0; i < i2s->active_channels; i++)
 339                img_i2s_in_ch_enable(i2s, i);
 340
 341        return 0;
 342}
 343
 344static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
 345        .trigger = img_i2s_in_trigger,
 346        .hw_params = img_i2s_in_hw_params,
 347        .set_fmt = img_i2s_in_set_fmt
 348};
 349
 350static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
 351{
 352        struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
 353
 354        snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data);
 355
 356        return 0;
 357}
 358
 359static const struct snd_soc_component_driver img_i2s_in_component = {
 360        .name = "img-i2s-in"
 361};
 362
 363static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
 364        struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
 365{
 366        unsigned int i2s_channels = params_channels(params) / 2;
 367        struct snd_soc_pcm_runtime *rtd = st->private_data;
 368        struct snd_dmaengine_dai_dma_data *dma_data;
 369        int ret;
 370
 371        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
 372
 373        ret = snd_hwparams_to_dma_slave_config(st, params, sc);
 374        if (ret)
 375                return ret;
 376
 377        sc->src_addr = dma_data->addr;
 378        sc->src_addr_width = dma_data->addr_width;
 379        sc->src_maxburst = 4 * i2s_channels;
 380
 381        return 0;
 382}
 383
 384static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
 385        .prepare_slave_config = img_i2s_in_dma_prepare_slave_config
 386};
 387
 388static int img_i2s_in_probe(struct platform_device *pdev)
 389{
 390        struct img_i2s_in *i2s;
 391        struct resource *res;
 392        void __iomem *base;
 393        int ret, i;
 394        struct reset_control *rst;
 395        unsigned int max_i2s_chan_pow_2;
 396        struct device *dev = &pdev->dev;
 397
 398        i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
 399        if (!i2s)
 400                return -ENOMEM;
 401
 402        platform_set_drvdata(pdev, i2s);
 403
 404        i2s->dev = dev;
 405
 406        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 407        base = devm_ioremap_resource(dev, res);
 408        if (IS_ERR(base))
 409                return PTR_ERR(base);
 410
 411        i2s->base = base;
 412
 413        if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
 414                        &i2s->max_i2s_chan)) {
 415                dev_err(dev, "No img,i2s-channels property\n");
 416                return -EINVAL;
 417        }
 418
 419        max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
 420
 421        i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
 422
 423        i2s->clk_sys = devm_clk_get(dev, "sys");
 424        if (IS_ERR(i2s->clk_sys)) {
 425                if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
 426                        dev_err(dev, "Failed to acquire clock 'sys'\n");
 427                return PTR_ERR(i2s->clk_sys);
 428        }
 429
 430        ret = clk_prepare_enable(i2s->clk_sys);
 431        if (ret)
 432                return ret;
 433
 434        i2s->active_channels = 1;
 435        i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
 436        i2s->dma_data.addr_width = 4;
 437
 438        i2s->dai_driver.probe = img_i2s_in_dai_probe;
 439        i2s->dai_driver.capture.channels_min = 2;
 440        i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
 441        i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
 442        i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE |
 443                SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
 444        i2s->dai_driver.ops = &img_i2s_in_dai_ops;
 445
 446        rst = devm_reset_control_get_exclusive(dev, "rst");
 447        if (IS_ERR(rst)) {
 448                if (PTR_ERR(rst) == -EPROBE_DEFER) {
 449                        ret = -EPROBE_DEFER;
 450                        goto err_clk_disable;
 451                }
 452
 453                dev_dbg(dev, "No top level reset found\n");
 454
 455                img_i2s_in_disable(i2s);
 456
 457                for (i = 0; i < i2s->max_i2s_chan; i++)
 458                        img_i2s_in_ch_disable(i2s, i);
 459        } else {
 460                reset_control_assert(rst);
 461                reset_control_deassert(rst);
 462        }
 463
 464        img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL);
 465
 466        for (i = 0; i < i2s->max_i2s_chan; i++)
 467                img_i2s_in_ch_writel(i2s, i,
 468                        (4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) |
 469                        IMG_I2S_IN_CH_CTL_JUST_MASK |
 470                        IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
 471
 472        ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
 473                                                &i2s->dai_driver, 1);
 474        if (ret)
 475                goto err_clk_disable;
 476
 477        ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
 478        if (ret)
 479                goto err_clk_disable;
 480
 481        return 0;
 482
 483err_clk_disable:
 484        clk_disable_unprepare(i2s->clk_sys);
 485
 486        return ret;
 487}
 488
 489static int img_i2s_in_dev_remove(struct platform_device *pdev)
 490{
 491        struct img_i2s_in *i2s = platform_get_drvdata(pdev);
 492
 493        clk_disable_unprepare(i2s->clk_sys);
 494
 495        return 0;
 496}
 497
 498static const struct of_device_id img_i2s_in_of_match[] = {
 499        { .compatible = "img,i2s-in" },
 500        {}
 501};
 502MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
 503
 504static struct platform_driver img_i2s_in_driver = {
 505        .driver = {
 506                .name = "img-i2s-in",
 507                .of_match_table = img_i2s_in_of_match
 508        },
 509        .probe = img_i2s_in_probe,
 510        .remove = img_i2s_in_dev_remove
 511};
 512module_platform_driver(img_i2s_in_driver);
 513
 514MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
 515MODULE_DESCRIPTION("IMG I2S Input Driver");
 516MODULE_LICENSE("GPL v2");
 517