linux/sound/soc/qcom/lpass-platform.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
   4 *
   5 * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
   6 */
   7
   8#include <linux/dma-mapping.h>
   9#include <linux/export.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <sound/pcm_params.h>
  14#include <linux/regmap.h>
  15#include <sound/soc.h>
  16#include "lpass-lpaif-reg.h"
  17#include "lpass.h"
  18
  19#define DRV_NAME "lpass-platform"
  20
  21struct lpass_pcm_data {
  22        int dma_ch;
  23        int i2s_port;
  24};
  25
  26#define LPASS_PLATFORM_BUFFER_SIZE      (24 *  2 * 1024)
  27#define LPASS_PLATFORM_PERIODS          2
  28
  29static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
  30        .info                   =       SNDRV_PCM_INFO_MMAP |
  31                                        SNDRV_PCM_INFO_MMAP_VALID |
  32                                        SNDRV_PCM_INFO_INTERLEAVED |
  33                                        SNDRV_PCM_INFO_PAUSE |
  34                                        SNDRV_PCM_INFO_RESUME,
  35        .formats                =       SNDRV_PCM_FMTBIT_S16 |
  36                                        SNDRV_PCM_FMTBIT_S24 |
  37                                        SNDRV_PCM_FMTBIT_S32,
  38        .rates                  =       SNDRV_PCM_RATE_8000_192000,
  39        .rate_min               =       8000,
  40        .rate_max               =       192000,
  41        .channels_min           =       1,
  42        .channels_max           =       8,
  43        .buffer_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE,
  44        .period_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE /
  45                                                LPASS_PLATFORM_PERIODS,
  46        .period_bytes_min       =       LPASS_PLATFORM_BUFFER_SIZE /
  47                                                LPASS_PLATFORM_PERIODS,
  48        .periods_min            =       LPASS_PLATFORM_PERIODS,
  49        .periods_max            =       LPASS_PLATFORM_PERIODS,
  50        .fifo_size              =       0,
  51};
  52
  53static int lpass_platform_alloc_dmactl_fields(struct device *dev,
  54                                         struct regmap *map)
  55{
  56        struct lpass_data *drvdata = dev_get_drvdata(dev);
  57        struct lpass_variant *v = drvdata->variant;
  58        struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
  59        int rval;
  60
  61        drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
  62                                          GFP_KERNEL);
  63        if (drvdata->rd_dmactl == NULL)
  64                return -ENOMEM;
  65
  66        drvdata->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
  67                                          GFP_KERNEL);
  68        if (drvdata->wr_dmactl == NULL)
  69                return -ENOMEM;
  70
  71        rd_dmactl = drvdata->rd_dmactl;
  72        wr_dmactl = drvdata->wr_dmactl;
  73
  74        rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf,
  75                                            &v->rdma_intf, 6);
  76        if (rval)
  77                return rval;
  78
  79        return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
  80                                            &v->wrdma_intf, 6);
  81}
  82
  83static int lpass_platform_alloc_hdmidmactl_fields(struct device *dev,
  84                                         struct regmap *map)
  85{
  86        struct lpass_data *drvdata = dev_get_drvdata(dev);
  87        struct lpass_variant *v = drvdata->variant;
  88        struct lpaif_dmactl *rd_dmactl;
  89
  90        rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), GFP_KERNEL);
  91        if (rd_dmactl == NULL)
  92                return -ENOMEM;
  93
  94        drvdata->hdmi_rd_dmactl = rd_dmactl;
  95
  96        return devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->bursten,
  97                                            &v->hdmi_rdma_bursten, 8);
  98}
  99
 100static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 101                                      struct snd_pcm_substream *substream)
 102{
 103        struct snd_pcm_runtime *runtime = substream->runtime;
 104        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 105        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 106        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 107        struct lpass_variant *v = drvdata->variant;
 108        int ret, dma_ch, dir = substream->stream;
 109        struct lpass_pcm_data *data;
 110        struct regmap *map;
 111        unsigned int dai_id = cpu_dai->driver->id;
 112
 113        component->id = dai_id;
 114        data = kzalloc(sizeof(*data), GFP_KERNEL);
 115        if (!data)
 116                return -ENOMEM;
 117
 118        data->i2s_port = cpu_dai->driver->id;
 119        runtime->private_data = data;
 120
 121        if (v->alloc_dma_channel)
 122                dma_ch = v->alloc_dma_channel(drvdata, dir, dai_id);
 123        else
 124                dma_ch = 0;
 125
 126        if (dma_ch < 0) {
 127                kfree(data);
 128                return dma_ch;
 129        }
 130
 131        if (cpu_dai->driver->id == LPASS_DP_RX) {
 132                map = drvdata->hdmiif_map;
 133                drvdata->hdmi_substream[dma_ch] = substream;
 134        } else {
 135                map = drvdata->lpaif_map;
 136                drvdata->substream[dma_ch] = substream;
 137        }
 138        data->dma_ch = dma_ch;
 139        ret = regmap_write(map,
 140                        LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0);
 141        if (ret) {
 142                dev_err(soc_runtime->dev,
 143                        "error writing to rdmactl reg: %d\n", ret);
 144                return ret;
 145        }
 146        snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
 147
 148        runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
 149
 150        ret = snd_pcm_hw_constraint_integer(runtime,
 151                        SNDRV_PCM_HW_PARAM_PERIODS);
 152        if (ret < 0) {
 153                kfree(data);
 154                dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
 155                        ret);
 156                return -EINVAL;
 157        }
 158
 159        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 160
 161        return 0;
 162}
 163
 164static int lpass_platform_pcmops_close(struct snd_soc_component *component,
 165                                       struct snd_pcm_substream *substream)
 166{
 167        struct snd_pcm_runtime *runtime = substream->runtime;
 168        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 169        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 170        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 171        struct lpass_variant *v = drvdata->variant;
 172        struct lpass_pcm_data *data;
 173        unsigned int dai_id = cpu_dai->driver->id;
 174
 175        data = runtime->private_data;
 176        if (dai_id == LPASS_DP_RX)
 177                drvdata->hdmi_substream[data->dma_ch] = NULL;
 178        else
 179                drvdata->substream[data->dma_ch] = NULL;
 180        if (v->free_dma_channel)
 181                v->free_dma_channel(drvdata, data->dma_ch, dai_id);
 182
 183        kfree(data);
 184        return 0;
 185}
 186
 187static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 188                                           struct snd_pcm_substream *substream,
 189                                           struct snd_pcm_hw_params *params)
 190{
 191        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 192        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 193        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 194        struct snd_pcm_runtime *rt = substream->runtime;
 195        struct lpass_pcm_data *pcm_data = rt->private_data;
 196        struct lpass_variant *v = drvdata->variant;
 197        snd_pcm_format_t format = params_format(params);
 198        unsigned int channels = params_channels(params);
 199        unsigned int regval;
 200        struct lpaif_dmactl *dmactl;
 201        int id, dir = substream->stream;
 202        int bitwidth;
 203        int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
 204        unsigned int dai_id = cpu_dai->driver->id;
 205
 206        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK) {
 207                id = pcm_data->dma_ch;
 208                if (dai_id == LPASS_DP_RX)
 209                        dmactl = drvdata->hdmi_rd_dmactl;
 210                else
 211                        dmactl = drvdata->rd_dmactl;
 212
 213        } else {
 214                dmactl = drvdata->wr_dmactl;
 215                id = pcm_data->dma_ch - v->wrdma_channel_start;
 216        }
 217
 218        bitwidth = snd_pcm_format_width(format);
 219        if (bitwidth < 0) {
 220                dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
 221                                bitwidth);
 222                return bitwidth;
 223        }
 224
 225        ret = regmap_fields_write(dmactl->bursten, id, LPAIF_DMACTL_BURSTEN_INCR4);
 226        if (ret) {
 227                dev_err(soc_runtime->dev, "error updating bursten field: %d\n", ret);
 228                return ret;
 229        }
 230
 231        ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
 232        if (ret) {
 233                dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret);
 234                return ret;
 235        }
 236
 237        switch (dai_id) {
 238        case LPASS_DP_RX:
 239                ret = regmap_fields_write(dmactl->burst8, id,
 240                                                        LPAIF_DMACTL_BURSTEN_INCR4);
 241                if (ret) {
 242                        dev_err(soc_runtime->dev, "error updating burst8en field: %d\n", ret);
 243                        return ret;
 244                }
 245                ret = regmap_fields_write(dmactl->burst16, id,
 246                                                        LPAIF_DMACTL_BURSTEN_INCR4);
 247                if (ret) {
 248                        dev_err(soc_runtime->dev, "error updating burst16en field: %d\n", ret);
 249                        return ret;
 250                }
 251                ret = regmap_fields_write(dmactl->dynburst, id,
 252                                                        LPAIF_DMACTL_BURSTEN_INCR4);
 253                if (ret) {
 254                        dev_err(soc_runtime->dev, "error updating dynbursten field: %d\n", ret);
 255                        return ret;
 256                }
 257                break;
 258        case MI2S_PRIMARY:
 259        case MI2S_SECONDARY:
 260                ret = regmap_fields_write(dmactl->intf, id,
 261                                                LPAIF_DMACTL_AUDINTF(dma_port));
 262                if (ret) {
 263                        dev_err(soc_runtime->dev, "error updating audio interface field: %d\n",
 264                                        ret);
 265                        return ret;
 266                }
 267
 268                break;
 269        default:
 270                dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai_id);
 271                break;
 272        }
 273        switch (bitwidth) {
 274        case 16:
 275                switch (channels) {
 276                case 1:
 277                case 2:
 278                        regval = LPAIF_DMACTL_WPSCNT_ONE;
 279                        break;
 280                case 4:
 281                        regval = LPAIF_DMACTL_WPSCNT_TWO;
 282                        break;
 283                case 6:
 284                        regval = LPAIF_DMACTL_WPSCNT_THREE;
 285                        break;
 286                case 8:
 287                        regval = LPAIF_DMACTL_WPSCNT_FOUR;
 288                        break;
 289                default:
 290                        dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
 291                                bitwidth, channels);
 292                        return -EINVAL;
 293                }
 294                break;
 295        case 24:
 296        case 32:
 297                switch (channels) {
 298                case 1:
 299                        regval = LPAIF_DMACTL_WPSCNT_ONE;
 300                        break;
 301                case 2:
 302                        regval = (dai_id == LPASS_DP_RX ?
 303                        LPAIF_DMACTL_WPSCNT_ONE :
 304                        LPAIF_DMACTL_WPSCNT_TWO);
 305                        break;
 306                case 4:
 307                        regval = (dai_id == LPASS_DP_RX ?
 308                        LPAIF_DMACTL_WPSCNT_TWO :
 309                        LPAIF_DMACTL_WPSCNT_FOUR);
 310                        break;
 311                case 6:
 312                        regval = (dai_id == LPASS_DP_RX ?
 313                        LPAIF_DMACTL_WPSCNT_THREE :
 314                        LPAIF_DMACTL_WPSCNT_SIX);
 315                        break;
 316                case 8:
 317                        regval = (dai_id == LPASS_DP_RX ?
 318                        LPAIF_DMACTL_WPSCNT_FOUR :
 319                        LPAIF_DMACTL_WPSCNT_EIGHT);
 320                        break;
 321                default:
 322                        dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
 323                                bitwidth, channels);
 324                        return -EINVAL;
 325                }
 326                break;
 327        default:
 328                dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
 329                        bitwidth, channels);
 330                return -EINVAL;
 331        }
 332
 333        ret = regmap_fields_write(dmactl->wpscnt, id, regval);
 334        if (ret) {
 335                dev_err(soc_runtime->dev, "error writing to dmactl reg: %d\n",
 336                        ret);
 337                return ret;
 338        }
 339
 340        return 0;
 341}
 342
 343static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component,
 344                                         struct snd_pcm_substream *substream)
 345{
 346        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 347        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 348        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 349        struct snd_pcm_runtime *rt = substream->runtime;
 350        struct lpass_pcm_data *pcm_data = rt->private_data;
 351        struct lpass_variant *v = drvdata->variant;
 352        unsigned int reg;
 353        int ret;
 354        struct regmap *map;
 355        unsigned int dai_id = cpu_dai->driver->id;
 356
 357        if (dai_id == LPASS_DP_RX)
 358                map = drvdata->hdmiif_map;
 359        else
 360                map = drvdata->lpaif_map;
 361
 362        reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id);
 363        ret = regmap_write(map, reg, 0);
 364        if (ret)
 365                dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
 366                        ret);
 367
 368        return ret;
 369}
 370
 371static int lpass_platform_pcmops_prepare(struct snd_soc_component *component,
 372                                         struct snd_pcm_substream *substream)
 373{
 374        struct snd_pcm_runtime *runtime = substream->runtime;
 375        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 376        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 377        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 378        struct snd_pcm_runtime *rt = substream->runtime;
 379        struct lpass_pcm_data *pcm_data = rt->private_data;
 380        struct lpass_variant *v = drvdata->variant;
 381        struct lpaif_dmactl *dmactl;
 382        struct regmap *map;
 383        int ret, id, ch, dir = substream->stream;
 384        unsigned int dai_id = cpu_dai->driver->id;
 385
 386
 387        ch = pcm_data->dma_ch;
 388        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK) {
 389                if (dai_id == LPASS_DP_RX) {
 390                        dmactl = drvdata->hdmi_rd_dmactl;
 391                        map = drvdata->hdmiif_map;
 392                } else {
 393                        dmactl = drvdata->rd_dmactl;
 394                        map = drvdata->lpaif_map;
 395                }
 396
 397                id = pcm_data->dma_ch;
 398        } else {
 399                dmactl = drvdata->wr_dmactl;
 400                id = pcm_data->dma_ch - v->wrdma_channel_start;
 401                map = drvdata->lpaif_map;
 402        }
 403
 404        ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id),
 405                                runtime->dma_addr);
 406        if (ret) {
 407                dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
 408                        ret);
 409                return ret;
 410        }
 411
 412        ret = regmap_write(map, LPAIF_DMABUFF_REG(v, ch, dir, dai_id),
 413                        (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 414        if (ret) {
 415                dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
 416                        ret);
 417                return ret;
 418        }
 419
 420        ret = regmap_write(map, LPAIF_DMAPER_REG(v, ch, dir, dai_id),
 421                        (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 422        if (ret) {
 423                dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
 424                        ret);
 425                return ret;
 426        }
 427
 428        ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON);
 429        if (ret) {
 430                dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
 431                        ret);
 432                return ret;
 433        }
 434
 435        return 0;
 436}
 437
 438static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
 439                                         struct snd_pcm_substream *substream,
 440                                         int cmd)
 441{
 442        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 443        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 444        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 445        struct snd_pcm_runtime *rt = substream->runtime;
 446        struct lpass_pcm_data *pcm_data = rt->private_data;
 447        struct lpass_variant *v = drvdata->variant;
 448        struct lpaif_dmactl *dmactl;
 449        struct regmap *map;
 450        int ret, ch, id;
 451        int dir = substream->stream;
 452        unsigned int reg_irqclr = 0, val_irqclr = 0;
 453        unsigned int  reg_irqen = 0, val_irqen = 0, val_mask = 0;
 454        unsigned int dai_id = cpu_dai->driver->id;
 455        unsigned int dma_ctrl_reg = 0;
 456
 457        ch = pcm_data->dma_ch;
 458        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK) {
 459                id = pcm_data->dma_ch;
 460                if (dai_id == LPASS_DP_RX) {
 461                        dmactl = drvdata->hdmi_rd_dmactl;
 462                        map = drvdata->hdmiif_map;
 463                } else {
 464                        dmactl = drvdata->rd_dmactl;
 465                        map = drvdata->lpaif_map;
 466                }
 467        } else {
 468                dmactl = drvdata->wr_dmactl;
 469                id = pcm_data->dma_ch - v->wrdma_channel_start;
 470                map = drvdata->lpaif_map;
 471        }
 472        ret = regmap_read(map, LPAIF_DMACTL_REG(v, ch, dir, dai_id), &dma_ctrl_reg);
 473        if (ret) {
 474                dev_err(soc_runtime->dev, "error reading from rdmactl reg: %d\n", ret);
 475                return ret;
 476        }
 477
 478        if (dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE ||
 479                dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE + 1) {
 480                dev_err(soc_runtime->dev, "error in rdmactl register state\n");
 481                return -ENOTRECOVERABLE;
 482        }
 483        switch (cmd) {
 484        case SNDRV_PCM_TRIGGER_START:
 485        case SNDRV_PCM_TRIGGER_RESUME:
 486        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 487                ret = regmap_fields_write(dmactl->enable, id,
 488                                                 LPAIF_DMACTL_ENABLE_ON);
 489                if (ret) {
 490                        dev_err(soc_runtime->dev,
 491                                "error writing to rdmactl reg: %d\n", ret);
 492                        return ret;
 493                }
 494                switch (dai_id) {
 495                case LPASS_DP_RX:
 496                        ret = regmap_fields_write(dmactl->dyncclk, id,
 497                                         LPAIF_DMACTL_DYNCLK_ON);
 498                        if (ret) {
 499                                dev_err(soc_runtime->dev,
 500                                        "error writing to rdmactl reg: %d\n", ret);
 501                                return ret;
 502                        }
 503                        map = drvdata->hdmiif_map;
 504                        reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
 505                        val_irqclr = (LPAIF_IRQ_ALL(ch) |
 506                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 507                                        LPAIF_IRQ_HDMI_METADONE |
 508                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 509
 510                        reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
 511                        val_mask = (LPAIF_IRQ_ALL(ch) |
 512                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 513                                        LPAIF_IRQ_HDMI_METADONE |
 514                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 515                        val_irqen = (LPAIF_IRQ_ALL(ch) |
 516                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 517                                        LPAIF_IRQ_HDMI_METADONE |
 518                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 519                        break;
 520                case MI2S_PRIMARY:
 521                case MI2S_SECONDARY:
 522                        map = drvdata->lpaif_map;
 523                        reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 524                        val_irqclr = LPAIF_IRQ_ALL(ch);
 525
 526
 527                        reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
 528                        val_mask = LPAIF_IRQ_ALL(ch);
 529                        val_irqen = LPAIF_IRQ_ALL(ch);
 530                        break;
 531                default:
 532                        dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
 533                        return -EINVAL;
 534                }
 535
 536                ret = regmap_write(map, reg_irqclr, val_irqclr);
 537                if (ret) {
 538                        dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", ret);
 539                        return ret;
 540                }
 541                ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen);
 542                if (ret) {
 543                        dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret);
 544                        return ret;
 545                }
 546                break;
 547        case SNDRV_PCM_TRIGGER_STOP:
 548        case SNDRV_PCM_TRIGGER_SUSPEND:
 549        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 550                ret = regmap_fields_write(dmactl->enable, id,
 551                                         LPAIF_DMACTL_ENABLE_OFF);
 552                if (ret) {
 553                        dev_err(soc_runtime->dev,
 554                                "error writing to rdmactl reg: %d\n", ret);
 555                        return ret;
 556                }
 557                switch (dai_id) {
 558                case LPASS_DP_RX:
 559                        ret = regmap_fields_write(dmactl->dyncclk, id,
 560                                         LPAIF_DMACTL_DYNCLK_OFF);
 561                        if (ret) {
 562                                dev_err(soc_runtime->dev,
 563                                        "error writing to rdmactl reg: %d\n", ret);
 564                                return ret;
 565                        }
 566                        map = drvdata->hdmiif_map;
 567                        reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
 568                        val_mask = (LPAIF_IRQ_ALL(ch) |
 569                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 570                                        LPAIF_IRQ_HDMI_METADONE |
 571                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 572                        val_irqen = 0;
 573                        break;
 574                case MI2S_PRIMARY:
 575                case MI2S_SECONDARY:
 576                        map = drvdata->lpaif_map;
 577                        reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
 578                        val_mask = LPAIF_IRQ_ALL(ch);
 579                        val_irqen = 0;
 580                        break;
 581                default:
 582                        dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
 583                        return -EINVAL;
 584                }
 585
 586                ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen);
 587                if (ret) {
 588                        dev_err(soc_runtime->dev,
 589                                "error writing to irqen reg: %d\n", ret);
 590                        return ret;
 591                }
 592                break;
 593        }
 594
 595        return 0;
 596}
 597
 598static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 599                struct snd_soc_component *component,
 600                struct snd_pcm_substream *substream)
 601{
 602        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 603        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 604        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 605        struct snd_pcm_runtime *rt = substream->runtime;
 606        struct lpass_pcm_data *pcm_data = rt->private_data;
 607        struct lpass_variant *v = drvdata->variant;
 608        unsigned int base_addr, curr_addr;
 609        int ret, ch, dir = substream->stream;
 610        struct regmap *map;
 611        unsigned int dai_id = cpu_dai->driver->id;
 612
 613        if (dai_id == LPASS_DP_RX)
 614                map = drvdata->hdmiif_map;
 615        else
 616                map = drvdata->lpaif_map;
 617
 618        ch = pcm_data->dma_ch;
 619
 620        ret = regmap_read(map,
 621                        LPAIF_DMABASE_REG(v, ch, dir, dai_id), &base_addr);
 622        if (ret) {
 623                dev_err(soc_runtime->dev,
 624                        "error reading from rdmabase reg: %d\n", ret);
 625                return ret;
 626        }
 627
 628        ret = regmap_read(map,
 629                        LPAIF_DMACURR_REG(v, ch, dir, dai_id), &curr_addr);
 630        if (ret) {
 631                dev_err(soc_runtime->dev,
 632                        "error reading from rdmacurr reg: %d\n", ret);
 633                return ret;
 634        }
 635
 636        return bytes_to_frames(substream->runtime, curr_addr - base_addr);
 637}
 638
 639static int lpass_platform_pcmops_mmap(struct snd_soc_component *component,
 640                                      struct snd_pcm_substream *substream,
 641                                      struct vm_area_struct *vma)
 642{
 643        struct snd_pcm_runtime *runtime = substream->runtime;
 644
 645        return dma_mmap_coherent(component->dev, vma, runtime->dma_area,
 646                                 runtime->dma_addr, runtime->dma_bytes);
 647}
 648
 649static irqreturn_t lpass_dma_interrupt_handler(
 650                        struct snd_pcm_substream *substream,
 651                        struct lpass_data *drvdata,
 652                        int chan, u32 interrupts)
 653{
 654        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 655        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 656        struct lpass_variant *v = drvdata->variant;
 657        irqreturn_t ret = IRQ_NONE;
 658        int rv;
 659        unsigned int reg = 0, val = 0;
 660        struct regmap *map;
 661        unsigned int dai_id = cpu_dai->driver->id;
 662
 663        switch (dai_id) {
 664        case LPASS_DP_RX:
 665                map = drvdata->hdmiif_map;
 666                reg = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
 667                val = (LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
 668                LPAIF_IRQ_HDMI_METADONE |
 669                LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan));
 670        break;
 671        case MI2S_PRIMARY:
 672        case MI2S_SECONDARY:
 673                map = drvdata->lpaif_map;
 674                reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 675                val = 0;
 676        break;
 677        default:
 678        dev_err(soc_runtime->dev, "%s: invalid  %d interface\n", __func__, dai_id);
 679        return -EINVAL;
 680        }
 681        if (interrupts & LPAIF_IRQ_PER(chan)) {
 682
 683                rv = regmap_write(map, reg, LPAIF_IRQ_PER(chan) | val);
 684                if (rv) {
 685                        dev_err(soc_runtime->dev,
 686                                "error writing to irqclear reg: %d\n", rv);
 687                        return IRQ_NONE;
 688                }
 689                snd_pcm_period_elapsed(substream);
 690                ret = IRQ_HANDLED;
 691        }
 692
 693        if (interrupts & LPAIF_IRQ_XRUN(chan)) {
 694                rv = regmap_write(map, reg, LPAIF_IRQ_XRUN(chan) | val);
 695                if (rv) {
 696                        dev_err(soc_runtime->dev,
 697                                "error writing to irqclear reg: %d\n", rv);
 698                        return IRQ_NONE;
 699                }
 700                dev_warn(soc_runtime->dev, "xrun warning\n");
 701                snd_pcm_stop_xrun(substream);
 702                ret = IRQ_HANDLED;
 703        }
 704
 705        if (interrupts & LPAIF_IRQ_ERR(chan)) {
 706                rv = regmap_write(map, reg, LPAIF_IRQ_ERR(chan) | val);
 707                if (rv) {
 708                        dev_err(soc_runtime->dev,
 709                                "error writing to irqclear reg: %d\n", rv);
 710                        return IRQ_NONE;
 711                }
 712                dev_err(soc_runtime->dev, "bus access error\n");
 713                snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
 714                ret = IRQ_HANDLED;
 715        }
 716
 717        if (interrupts & val) {
 718                rv = regmap_write(map, reg, val);
 719                if (rv) {
 720                        dev_err(soc_runtime->dev,
 721                        "error writing to irqclear reg: %d\n", rv);
 722                        return IRQ_NONE;
 723                }
 724                ret = IRQ_HANDLED;
 725        }
 726
 727        return ret;
 728}
 729
 730static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 731{
 732        struct lpass_data *drvdata = data;
 733        struct lpass_variant *v = drvdata->variant;
 734        unsigned int irqs;
 735        int rv, chan;
 736
 737        rv = regmap_read(drvdata->lpaif_map,
 738                        LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
 739        if (rv) {
 740                pr_err("error reading from irqstat reg: %d\n", rv);
 741                return IRQ_NONE;
 742        }
 743
 744        /* Handle per channel interrupts */
 745        for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
 746                if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
 747                        rv = lpass_dma_interrupt_handler(
 748                                                drvdata->substream[chan],
 749                                                drvdata, chan, irqs);
 750                        if (rv != IRQ_HANDLED)
 751                                return rv;
 752                }
 753        }
 754
 755        return IRQ_HANDLED;
 756}
 757
 758static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data)
 759{
 760        struct lpass_data *drvdata = data;
 761        struct lpass_variant *v = drvdata->variant;
 762        unsigned int irqs;
 763        int rv, chan;
 764
 765        rv = regmap_read(drvdata->hdmiif_map,
 766                        LPASS_HDMITX_APP_IRQSTAT_REG(v), &irqs);
 767        if (rv) {
 768                pr_err("error reading from irqstat reg: %d\n", rv);
 769                return IRQ_NONE;
 770        }
 771
 772        /* Handle per channel interrupts */
 773        for (chan = 0; chan < LPASS_MAX_HDMI_DMA_CHANNELS; chan++) {
 774                if (irqs & (LPAIF_IRQ_ALL(chan) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
 775                                LPAIF_IRQ_HDMI_METADONE |
 776                                LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan))
 777                        && drvdata->hdmi_substream[chan]) {
 778                        rv = lpass_dma_interrupt_handler(
 779                                                drvdata->hdmi_substream[chan],
 780                                                drvdata, chan, irqs);
 781                        if (rv != IRQ_HANDLED)
 782                                return rv;
 783                }
 784        }
 785
 786        return IRQ_HANDLED;
 787}
 788
 789static int lpass_platform_pcm_new(struct snd_soc_component *component,
 790                                  struct snd_soc_pcm_runtime *soc_runtime)
 791{
 792        struct snd_pcm *pcm = soc_runtime->pcm;
 793        struct snd_pcm_substream *psubstream, *csubstream;
 794        int ret = -EINVAL;
 795        size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
 796
 797        psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 798        if (psubstream) {
 799                ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
 800                                        component->dev,
 801                                        size, &psubstream->dma_buffer);
 802                if (ret) {
 803                        dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
 804                        return ret;
 805                }
 806        }
 807
 808        csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
 809        if (csubstream) {
 810                ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
 811                                        component->dev,
 812                                        size, &csubstream->dma_buffer);
 813                if (ret) {
 814                        dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
 815                        if (psubstream)
 816                                snd_dma_free_pages(&psubstream->dma_buffer);
 817                        return ret;
 818                }
 819
 820        }
 821
 822        return 0;
 823}
 824
 825static void lpass_platform_pcm_free(struct snd_soc_component *component,
 826                                    struct snd_pcm *pcm)
 827{
 828        struct snd_pcm_substream *substream;
 829        int i;
 830
 831        for_each_pcm_streams(i) {
 832                substream = pcm->streams[i].substream;
 833                if (substream) {
 834                        snd_dma_free_pages(&substream->dma_buffer);
 835                        substream->dma_buffer.area = NULL;
 836                        substream->dma_buffer.addr = 0;
 837                }
 838        }
 839}
 840
 841static const struct snd_soc_component_driver lpass_component_driver = {
 842        .name           = DRV_NAME,
 843        .open           = lpass_platform_pcmops_open,
 844        .close          = lpass_platform_pcmops_close,
 845        .hw_params      = lpass_platform_pcmops_hw_params,
 846        .hw_free        = lpass_platform_pcmops_hw_free,
 847        .prepare        = lpass_platform_pcmops_prepare,
 848        .trigger        = lpass_platform_pcmops_trigger,
 849        .pointer        = lpass_platform_pcmops_pointer,
 850        .mmap           = lpass_platform_pcmops_mmap,
 851        .pcm_construct  = lpass_platform_pcm_new,
 852        .pcm_destruct   = lpass_platform_pcm_free,
 853
 854};
 855
 856int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 857{
 858        struct lpass_data *drvdata = platform_get_drvdata(pdev);
 859        struct lpass_variant *v = drvdata->variant;
 860        int ret;
 861
 862        drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 863        if (drvdata->lpaif_irq < 0)
 864                return -ENODEV;
 865
 866        /* ensure audio hardware is disabled */
 867        ret = regmap_write(drvdata->lpaif_map,
 868                        LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
 869        if (ret) {
 870                dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
 871                return ret;
 872        }
 873
 874        ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
 875                        lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
 876                        "lpass-irq-lpaif", drvdata);
 877        if (ret) {
 878                dev_err(&pdev->dev, "irq request failed: %d\n", ret);
 879                return ret;
 880        }
 881
 882        ret = lpass_platform_alloc_dmactl_fields(&pdev->dev,
 883                                                 drvdata->lpaif_map);
 884        if (ret) {
 885                dev_err(&pdev->dev,
 886                        "error initializing dmactl fields: %d\n", ret);
 887                return ret;
 888        }
 889
 890        if (drvdata->hdmi_port_enable) {
 891                drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi");
 892                if (drvdata->hdmiif_irq < 0)
 893                        return -ENODEV;
 894
 895                ret = devm_request_irq(&pdev->dev, drvdata->hdmiif_irq,
 896                                lpass_platform_hdmiif_irq, 0, "lpass-irq-hdmi", drvdata);
 897                if (ret) {
 898                        dev_err(&pdev->dev, "irq hdmi request failed: %d\n", ret);
 899                        return ret;
 900                }
 901                ret = regmap_write(drvdata->hdmiif_map,
 902                                LPASS_HDMITX_APP_IRQEN_REG(v), 0);
 903                if (ret) {
 904                        dev_err(&pdev->dev, "error writing to hdmi irqen reg: %d\n", ret);
 905                        return ret;
 906                }
 907
 908                ret = lpass_platform_alloc_hdmidmactl_fields(&pdev->dev,
 909                                                         drvdata->hdmiif_map);
 910                if (ret) {
 911                        dev_err(&pdev->dev,
 912                                "error initializing hdmidmactl fields: %d\n", ret);
 913                        return ret;
 914                }
 915        }
 916        return devm_snd_soc_register_component(&pdev->dev,
 917                        &lpass_component_driver, NULL, 0);
 918}
 919EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
 920
 921MODULE_DESCRIPTION("QTi LPASS Platform Driver");
 922MODULE_LICENSE("GPL v2");
 923