linux/sound/soc/qcom/lpass-platform.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 and
   6 * only version 2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
  14 */
  15
  16#include <linux/dma-mapping.h>
  17#include <linux/export.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21#include <sound/pcm_params.h>
  22#include <linux/regmap.h>
  23#include <sound/soc.h>
  24#include "lpass-lpaif-reg.h"
  25#include "lpass.h"
  26
  27struct lpass_pcm_data {
  28        int rdma_ch;
  29        int wrdma_ch;
  30        int i2s_port;
  31};
  32
  33#define LPASS_PLATFORM_BUFFER_SIZE      (16 * 1024)
  34#define LPASS_PLATFORM_PERIODS          2
  35
  36static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
  37        .info                   =       SNDRV_PCM_INFO_MMAP |
  38                                        SNDRV_PCM_INFO_MMAP_VALID |
  39                                        SNDRV_PCM_INFO_INTERLEAVED |
  40                                        SNDRV_PCM_INFO_PAUSE |
  41                                        SNDRV_PCM_INFO_RESUME,
  42        .formats                =       SNDRV_PCM_FMTBIT_S16 |
  43                                        SNDRV_PCM_FMTBIT_S24 |
  44                                        SNDRV_PCM_FMTBIT_S32,
  45        .rates                  =       SNDRV_PCM_RATE_8000_192000,
  46        .rate_min               =       8000,
  47        .rate_max               =       192000,
  48        .channels_min           =       1,
  49        .channels_max           =       8,
  50        .buffer_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE,
  51        .period_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE /
  52                                                LPASS_PLATFORM_PERIODS,
  53        .period_bytes_min       =       LPASS_PLATFORM_BUFFER_SIZE /
  54                                                LPASS_PLATFORM_PERIODS,
  55        .periods_min            =       LPASS_PLATFORM_PERIODS,
  56        .periods_max            =       LPASS_PLATFORM_PERIODS,
  57        .fifo_size              =       0,
  58};
  59
  60static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
  61{
  62        struct snd_pcm_runtime *runtime = substream->runtime;
  63        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  64        int ret;
  65
  66        snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
  67
  68        runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
  69
  70        ret = snd_pcm_hw_constraint_integer(runtime,
  71                        SNDRV_PCM_HW_PARAM_PERIODS);
  72        if (ret < 0) {
  73                dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n",
  74                                __func__, ret);
  75                return -EINVAL;
  76        }
  77
  78        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  79
  80        return 0;
  81}
  82
  83static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
  84                struct snd_pcm_hw_params *params)
  85{
  86        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  87        struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
  88        struct lpass_data *drvdata =
  89                snd_soc_platform_get_drvdata(soc_runtime->platform);
  90        struct lpass_variant *v = drvdata->variant;
  91        snd_pcm_format_t format = params_format(params);
  92        unsigned int channels = params_channels(params);
  93        unsigned int regval;
  94        int ch, dir = substream->stream;
  95        int bitwidth;
  96        int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
  97
  98        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK)
  99                ch = pcm_data->rdma_ch;
 100        else
 101                ch = pcm_data->wrdma_ch;
 102
 103        bitwidth = snd_pcm_format_width(format);
 104        if (bitwidth < 0) {
 105                dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
 106                                __func__, bitwidth);
 107                return bitwidth;
 108        }
 109
 110        regval = LPAIF_DMACTL_BURSTEN_INCR4 |
 111                        LPAIF_DMACTL_AUDINTF(dma_port) |
 112                        LPAIF_DMACTL_FIFOWM_8;
 113
 114        switch (bitwidth) {
 115        case 16:
 116                switch (channels) {
 117                case 1:
 118                case 2:
 119                        regval |= LPAIF_DMACTL_WPSCNT_ONE;
 120                        break;
 121                case 4:
 122                        regval |= LPAIF_DMACTL_WPSCNT_TWO;
 123                        break;
 124                case 6:
 125                        regval |= LPAIF_DMACTL_WPSCNT_THREE;
 126                        break;
 127                case 8:
 128                        regval |= LPAIF_DMACTL_WPSCNT_FOUR;
 129                        break;
 130                default:
 131                        dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
 132                                        __func__, bitwidth, channels);
 133                        return -EINVAL;
 134                }
 135                break;
 136        case 24:
 137        case 32:
 138                switch (channels) {
 139                case 1:
 140                        regval |= LPAIF_DMACTL_WPSCNT_ONE;
 141                        break;
 142                case 2:
 143                        regval |= LPAIF_DMACTL_WPSCNT_TWO;
 144                        break;
 145                case 4:
 146                        regval |= LPAIF_DMACTL_WPSCNT_FOUR;
 147                        break;
 148                case 6:
 149                        regval |= LPAIF_DMACTL_WPSCNT_SIX;
 150                        break;
 151                case 8:
 152                        regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
 153                        break;
 154                default:
 155                        dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
 156                                        __func__, bitwidth, channels);
 157                        return -EINVAL;
 158                }
 159                break;
 160        default:
 161                dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
 162                                __func__, bitwidth, channels);
 163                return -EINVAL;
 164        }
 165
 166        ret = regmap_write(drvdata->lpaif_map,
 167                        LPAIF_DMACTL_REG(v, ch, dir), regval);
 168        if (ret) {
 169                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 170                                __func__, ret);
 171                return ret;
 172        }
 173
 174        return 0;
 175}
 176
 177static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
 178{
 179        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 180        struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 181        struct lpass_data *drvdata =
 182                snd_soc_platform_get_drvdata(soc_runtime->platform);
 183        struct lpass_variant *v = drvdata->variant;
 184        unsigned int reg;
 185        int ret;
 186
 187        if (substream->stream ==  SNDRV_PCM_STREAM_PLAYBACK)
 188                reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch);
 189        else
 190                reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch);
 191
 192        ret = regmap_write(drvdata->lpaif_map, reg, 0);
 193        if (ret)
 194                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 195                                __func__, ret);
 196
 197        return ret;
 198}
 199
 200static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 201{
 202        struct snd_pcm_runtime *runtime = substream->runtime;
 203        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 204        struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 205        struct lpass_data *drvdata =
 206                snd_soc_platform_get_drvdata(soc_runtime->platform);
 207        struct lpass_variant *v = drvdata->variant;
 208        int ret, ch, dir = substream->stream;
 209
 210        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK)
 211                ch = pcm_data->rdma_ch;
 212        else
 213                ch = pcm_data->wrdma_ch;
 214
 215        ret = regmap_write(drvdata->lpaif_map,
 216                        LPAIF_DMABASE_REG(v, ch, dir),
 217                        runtime->dma_addr);
 218        if (ret) {
 219                dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
 220                                __func__, ret);
 221                return ret;
 222        }
 223
 224        ret = regmap_write(drvdata->lpaif_map,
 225                        LPAIF_DMABUFF_REG(v, ch, dir),
 226                        (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 227        if (ret) {
 228                dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
 229                                __func__, ret);
 230                return ret;
 231        }
 232
 233        ret = regmap_write(drvdata->lpaif_map,
 234                        LPAIF_DMAPER_REG(v, ch, dir),
 235                        (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 236        if (ret) {
 237                dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
 238                                __func__, ret);
 239                return ret;
 240        }
 241
 242        ret = regmap_update_bits(drvdata->lpaif_map,
 243                        LPAIF_DMACTL_REG(v, ch, dir),
 244                        LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
 245        if (ret) {
 246                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 247                                __func__, ret);
 248                return ret;
 249        }
 250
 251        return 0;
 252}
 253
 254static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 255                int cmd)
 256{
 257        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 258        struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 259        struct lpass_data *drvdata =
 260                snd_soc_platform_get_drvdata(soc_runtime->platform);
 261        struct lpass_variant *v = drvdata->variant;
 262        int ret, ch, dir = substream->stream;
 263
 264        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 265                ch = pcm_data->rdma_ch;
 266        else
 267                ch = pcm_data->wrdma_ch;
 268
 269        switch (cmd) {
 270        case SNDRV_PCM_TRIGGER_START:
 271        case SNDRV_PCM_TRIGGER_RESUME:
 272        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 273                /* clear status before enabling interrupts */
 274                ret = regmap_write(drvdata->lpaif_map,
 275                                LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
 276                                LPAIF_IRQ_ALL(ch));
 277                if (ret) {
 278                        dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 279                                        __func__, ret);
 280                        return ret;
 281                }
 282
 283                ret = regmap_update_bits(drvdata->lpaif_map,
 284                                LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
 285                                LPAIF_IRQ_ALL(ch),
 286                                LPAIF_IRQ_ALL(ch));
 287                if (ret) {
 288                        dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 289                                        __func__, ret);
 290                        return ret;
 291                }
 292
 293                ret = regmap_update_bits(drvdata->lpaif_map,
 294                                LPAIF_DMACTL_REG(v, ch, dir),
 295                                LPAIF_DMACTL_ENABLE_MASK,
 296                                LPAIF_DMACTL_ENABLE_ON);
 297                if (ret) {
 298                        dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 299                                        __func__, ret);
 300                        return ret;
 301                }
 302                break;
 303        case SNDRV_PCM_TRIGGER_STOP:
 304        case SNDRV_PCM_TRIGGER_SUSPEND:
 305        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 306                ret = regmap_update_bits(drvdata->lpaif_map,
 307                                LPAIF_DMACTL_REG(v, ch, dir),
 308                                LPAIF_DMACTL_ENABLE_MASK,
 309                                LPAIF_DMACTL_ENABLE_OFF);
 310                if (ret) {
 311                        dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 312                                        __func__, ret);
 313                        return ret;
 314                }
 315
 316                ret = regmap_update_bits(drvdata->lpaif_map,
 317                                LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
 318                                LPAIF_IRQ_ALL(ch), 0);
 319                if (ret) {
 320                        dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 321                                        __func__, ret);
 322                        return ret;
 323                }
 324                break;
 325        }
 326
 327        return 0;
 328}
 329
 330static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 331                struct snd_pcm_substream *substream)
 332{
 333        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 334        struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 335        struct lpass_data *drvdata =
 336                        snd_soc_platform_get_drvdata(soc_runtime->platform);
 337        struct lpass_variant *v = drvdata->variant;
 338        unsigned int base_addr, curr_addr;
 339        int ret, ch, dir = substream->stream;
 340
 341        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 342                ch = pcm_data->rdma_ch;
 343        else
 344                ch = pcm_data->wrdma_ch;
 345
 346        ret = regmap_read(drvdata->lpaif_map,
 347                        LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
 348        if (ret) {
 349                dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
 350                                __func__, ret);
 351                return ret;
 352        }
 353
 354        ret = regmap_read(drvdata->lpaif_map,
 355                        LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
 356        if (ret) {
 357                dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
 358                                __func__, ret);
 359                return ret;
 360        }
 361
 362        return bytes_to_frames(substream->runtime, curr_addr - base_addr);
 363}
 364
 365static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
 366                struct vm_area_struct *vma)
 367{
 368        struct snd_pcm_runtime *runtime = substream->runtime;
 369
 370        return dma_mmap_coherent(substream->pcm->card->dev, vma,
 371                        runtime->dma_area, runtime->dma_addr,
 372                        runtime->dma_bytes);
 373}
 374
 375static struct snd_pcm_ops lpass_platform_pcm_ops = {
 376        .open           = lpass_platform_pcmops_open,
 377        .ioctl          = snd_pcm_lib_ioctl,
 378        .hw_params      = lpass_platform_pcmops_hw_params,
 379        .hw_free        = lpass_platform_pcmops_hw_free,
 380        .prepare        = lpass_platform_pcmops_prepare,
 381        .trigger        = lpass_platform_pcmops_trigger,
 382        .pointer        = lpass_platform_pcmops_pointer,
 383        .mmap           = lpass_platform_pcmops_mmap,
 384};
 385
 386static irqreturn_t lpass_dma_interrupt_handler(
 387                        struct snd_pcm_substream *substream,
 388                        struct lpass_data *drvdata,
 389                        int chan, u32 interrupts)
 390{
 391        struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 392        struct lpass_variant *v = drvdata->variant;
 393        irqreturn_t ret = IRQ_NONE;
 394        int rv;
 395
 396        if (interrupts & LPAIF_IRQ_PER(chan)) {
 397                rv = regmap_write(drvdata->lpaif_map,
 398                                LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
 399                                LPAIF_IRQ_PER(chan));
 400                if (rv) {
 401                        dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 402                                        __func__, rv);
 403                        return IRQ_NONE;
 404                }
 405                snd_pcm_period_elapsed(substream);
 406                ret = IRQ_HANDLED;
 407        }
 408
 409        if (interrupts & LPAIF_IRQ_XRUN(chan)) {
 410                rv = regmap_write(drvdata->lpaif_map,
 411                                LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
 412                                LPAIF_IRQ_XRUN(chan));
 413                if (rv) {
 414                        dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 415                                        __func__, rv);
 416                        return IRQ_NONE;
 417                }
 418                dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__);
 419                snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
 420                ret = IRQ_HANDLED;
 421        }
 422
 423        if (interrupts & LPAIF_IRQ_ERR(chan)) {
 424                rv = regmap_write(drvdata->lpaif_map,
 425                                LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
 426                                LPAIF_IRQ_ERR(chan));
 427                if (rv) {
 428                        dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 429                                        __func__, rv);
 430                        return IRQ_NONE;
 431                }
 432                dev_err(soc_runtime->dev, "%s() bus access error\n", __func__);
 433                snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
 434                ret = IRQ_HANDLED;
 435        }
 436
 437        return ret;
 438}
 439
 440static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 441{
 442        struct lpass_data *drvdata = data;
 443        struct lpass_variant *v = drvdata->variant;
 444        unsigned int irqs;
 445        int rv, chan;
 446
 447        rv = regmap_read(drvdata->lpaif_map,
 448                        LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
 449        if (rv) {
 450                pr_err("%s() error reading from irqstat reg: %d\n",
 451                                __func__, rv);
 452                return IRQ_NONE;
 453        }
 454
 455        /* Handle per channel interrupts */
 456        for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
 457                if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
 458                        rv = lpass_dma_interrupt_handler(
 459                                                drvdata->substream[chan],
 460                                                drvdata, chan, irqs);
 461                        if (rv != IRQ_HANDLED)
 462                                return rv;
 463                }
 464        }
 465
 466        return IRQ_HANDLED;
 467}
 468
 469static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 470{
 471        struct snd_pcm *pcm = soc_runtime->pcm;
 472        struct snd_pcm_substream *psubstream, *csubstream;
 473        struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
 474        struct lpass_data *drvdata =
 475                snd_soc_platform_get_drvdata(soc_runtime->platform);
 476        struct lpass_variant *v = drvdata->variant;
 477        int ret = -EINVAL;
 478        struct lpass_pcm_data *data;
 479        size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
 480
 481        data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
 482        if (!data)
 483                return -ENOMEM;
 484
 485        data->i2s_port = cpu_dai->driver->id;
 486        snd_soc_pcm_set_drvdata(soc_runtime, data);
 487
 488        psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 489        if (psubstream) {
 490                if (v->alloc_dma_channel)
 491                        data->rdma_ch = v->alloc_dma_channel(drvdata,
 492                                                SNDRV_PCM_STREAM_PLAYBACK);
 493
 494                if (data->rdma_ch < 0)
 495                        return data->rdma_ch;
 496
 497                drvdata->substream[data->rdma_ch] = psubstream;
 498
 499                ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
 500                                        soc_runtime->platform->dev,
 501                                        size, &psubstream->dma_buffer);
 502                if (ret)
 503                        goto playback_alloc_err;
 504
 505                ret = regmap_write(drvdata->lpaif_map,
 506                        LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
 507                if (ret) {
 508                        dev_err(soc_runtime->dev,
 509                                "%s() error writing to rdmactl reg: %d\n",
 510                                __func__, ret);
 511                        goto capture_alloc_err;
 512                }
 513        }
 514
 515        csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
 516        if (csubstream) {
 517                if (v->alloc_dma_channel)
 518                        data->wrdma_ch = v->alloc_dma_channel(drvdata,
 519                                                SNDRV_PCM_STREAM_CAPTURE);
 520
 521                if (data->wrdma_ch < 0) {
 522                        ret = data->wrdma_ch;
 523                        goto capture_alloc_err;
 524                }
 525
 526                drvdata->substream[data->wrdma_ch] = csubstream;
 527
 528                ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
 529                                        soc_runtime->platform->dev,
 530                                        size, &csubstream->dma_buffer);
 531                if (ret)
 532                        goto capture_alloc_err;
 533
 534                ret = regmap_write(drvdata->lpaif_map,
 535                        LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0);
 536                if (ret) {
 537                        dev_err(soc_runtime->dev,
 538                                "%s() error writing to wrdmactl reg: %d\n",
 539                                __func__, ret);
 540                        goto capture_reg_err;
 541                }
 542        }
 543
 544        return 0;
 545
 546capture_reg_err:
 547        if (csubstream)
 548                snd_dma_free_pages(&csubstream->dma_buffer);
 549
 550capture_alloc_err:
 551        if (psubstream)
 552                snd_dma_free_pages(&psubstream->dma_buffer);
 553
 554 playback_alloc_err:
 555        dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
 556
 557        return ret;
 558}
 559
 560static void lpass_platform_pcm_free(struct snd_pcm *pcm)
 561{
 562        struct snd_soc_pcm_runtime *rt;
 563        struct lpass_data *drvdata;
 564        struct lpass_pcm_data *data;
 565        struct lpass_variant *v;
 566        struct snd_pcm_substream *substream;
 567        int ch, i;
 568
 569        for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
 570                substream = pcm->streams[i].substream;
 571                if (substream) {
 572                        rt = substream->private_data;
 573                        data = snd_soc_pcm_get_drvdata(rt);
 574                        drvdata = snd_soc_platform_get_drvdata(rt->platform);
 575
 576                        ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 577                                ? data->rdma_ch
 578                                : data->wrdma_ch;
 579                        v = drvdata->variant;
 580                        drvdata->substream[ch] = NULL;
 581                        if (v->free_dma_channel)
 582                                v->free_dma_channel(drvdata, ch);
 583
 584                        snd_dma_free_pages(&substream->dma_buffer);
 585                        substream->dma_buffer.area = NULL;
 586                        substream->dma_buffer.addr = 0;
 587                }
 588        }
 589}
 590
 591static struct snd_soc_platform_driver lpass_platform_driver = {
 592        .pcm_new        = lpass_platform_pcm_new,
 593        .pcm_free       = lpass_platform_pcm_free,
 594        .ops            = &lpass_platform_pcm_ops,
 595};
 596
 597int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 598{
 599        struct lpass_data *drvdata = platform_get_drvdata(pdev);
 600        struct lpass_variant *v = drvdata->variant;
 601        int ret;
 602
 603        drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 604        if (drvdata->lpaif_irq < 0) {
 605                dev_err(&pdev->dev, "%s() error getting irq handle: %d\n",
 606                                __func__, drvdata->lpaif_irq);
 607                return -ENODEV;
 608        }
 609
 610        /* ensure audio hardware is disabled */
 611        ret = regmap_write(drvdata->lpaif_map,
 612                        LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
 613        if (ret) {
 614                dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
 615                                __func__, ret);
 616                return ret;
 617        }
 618
 619        ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
 620                        lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
 621                        "lpass-irq-lpaif", drvdata);
 622        if (ret) {
 623                dev_err(&pdev->dev, "%s() irq request failed: %d\n",
 624                                __func__, ret);
 625                return ret;
 626        }
 627
 628
 629        return devm_snd_soc_register_platform(&pdev->dev,
 630                        &lpass_platform_driver);
 631}
 632EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
 633
 634MODULE_DESCRIPTION("QTi LPASS Platform Driver");
 635MODULE_LICENSE("GPL v2");
 636