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        return 0;
 160}
 161
 162static int lpass_platform_pcmops_close(struct snd_soc_component *component,
 163                                       struct snd_pcm_substream *substream)
 164{
 165        struct snd_pcm_runtime *runtime = substream->runtime;
 166        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 167        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 168        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 169        struct lpass_variant *v = drvdata->variant;
 170        struct lpass_pcm_data *data;
 171        unsigned int dai_id = cpu_dai->driver->id;
 172
 173        data = runtime->private_data;
 174        if (dai_id == LPASS_DP_RX)
 175                drvdata->hdmi_substream[data->dma_ch] = NULL;
 176        else
 177                drvdata->substream[data->dma_ch] = NULL;
 178        if (v->free_dma_channel)
 179                v->free_dma_channel(drvdata, data->dma_ch, dai_id);
 180
 181        kfree(data);
 182        return 0;
 183}
 184
 185static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 186                                           struct snd_pcm_substream *substream,
 187                                           struct snd_pcm_hw_params *params)
 188{
 189        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 190        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 191        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 192        struct snd_pcm_runtime *rt = substream->runtime;
 193        struct lpass_pcm_data *pcm_data = rt->private_data;
 194        struct lpass_variant *v = drvdata->variant;
 195        snd_pcm_format_t format = params_format(params);
 196        unsigned int channels = params_channels(params);
 197        unsigned int regval;
 198        struct lpaif_dmactl *dmactl;
 199        int id, dir = substream->stream;
 200        int bitwidth;
 201        int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
 202        unsigned int dai_id = cpu_dai->driver->id;
 203
 204        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK) {
 205                id = pcm_data->dma_ch;
 206                if (dai_id == LPASS_DP_RX)
 207                        dmactl = drvdata->hdmi_rd_dmactl;
 208                else
 209                        dmactl = drvdata->rd_dmactl;
 210
 211        } else {
 212                dmactl = drvdata->wr_dmactl;
 213                id = pcm_data->dma_ch - v->wrdma_channel_start;
 214        }
 215
 216        bitwidth = snd_pcm_format_width(format);
 217        if (bitwidth < 0) {
 218                dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
 219                                bitwidth);
 220                return bitwidth;
 221        }
 222
 223        ret = regmap_fields_write(dmactl->bursten, id, LPAIF_DMACTL_BURSTEN_INCR4);
 224        if (ret) {
 225                dev_err(soc_runtime->dev, "error updating bursten field: %d\n", ret);
 226                return ret;
 227        }
 228
 229        ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
 230        if (ret) {
 231                dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret);
 232                return ret;
 233        }
 234
 235        switch (dai_id) {
 236        case LPASS_DP_RX:
 237                ret = regmap_fields_write(dmactl->burst8, id,
 238                                                        LPAIF_DMACTL_BURSTEN_INCR4);
 239                if (ret) {
 240                        dev_err(soc_runtime->dev, "error updating burst8en field: %d\n", ret);
 241                        return ret;
 242                }
 243                ret = regmap_fields_write(dmactl->burst16, id,
 244                                                        LPAIF_DMACTL_BURSTEN_INCR4);
 245                if (ret) {
 246                        dev_err(soc_runtime->dev, "error updating burst16en field: %d\n", ret);
 247                        return ret;
 248                }
 249                ret = regmap_fields_write(dmactl->dynburst, id,
 250                                                        LPAIF_DMACTL_BURSTEN_INCR4);
 251                if (ret) {
 252                        dev_err(soc_runtime->dev, "error updating dynbursten field: %d\n", ret);
 253                        return ret;
 254                }
 255                break;
 256        case MI2S_PRIMARY:
 257        case MI2S_SECONDARY:
 258        case MI2S_TERTIARY:
 259        case MI2S_QUATERNARY:
 260        case MI2S_QUINARY:
 261                ret = regmap_fields_write(dmactl->intf, id,
 262                                                LPAIF_DMACTL_AUDINTF(dma_port));
 263                if (ret) {
 264                        dev_err(soc_runtime->dev, "error updating audio interface field: %d\n",
 265                                        ret);
 266                        return ret;
 267                }
 268
 269                break;
 270        default:
 271                dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai_id);
 272                break;
 273        }
 274        switch (bitwidth) {
 275        case 16:
 276                switch (channels) {
 277                case 1:
 278                case 2:
 279                        regval = LPAIF_DMACTL_WPSCNT_ONE;
 280                        break;
 281                case 4:
 282                        regval = LPAIF_DMACTL_WPSCNT_TWO;
 283                        break;
 284                case 6:
 285                        regval = LPAIF_DMACTL_WPSCNT_THREE;
 286                        break;
 287                case 8:
 288                        regval = LPAIF_DMACTL_WPSCNT_FOUR;
 289                        break;
 290                default:
 291                        dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
 292                                bitwidth, channels);
 293                        return -EINVAL;
 294                }
 295                break;
 296        case 24:
 297        case 32:
 298                switch (channels) {
 299                case 1:
 300                        regval = LPAIF_DMACTL_WPSCNT_ONE;
 301                        break;
 302                case 2:
 303                        regval = (dai_id == LPASS_DP_RX ?
 304                        LPAIF_DMACTL_WPSCNT_ONE :
 305                        LPAIF_DMACTL_WPSCNT_TWO);
 306                        break;
 307                case 4:
 308                        regval = (dai_id == LPASS_DP_RX ?
 309                        LPAIF_DMACTL_WPSCNT_TWO :
 310                        LPAIF_DMACTL_WPSCNT_FOUR);
 311                        break;
 312                case 6:
 313                        regval = (dai_id == LPASS_DP_RX ?
 314                        LPAIF_DMACTL_WPSCNT_THREE :
 315                        LPAIF_DMACTL_WPSCNT_SIX);
 316                        break;
 317                case 8:
 318                        regval = (dai_id == LPASS_DP_RX ?
 319                        LPAIF_DMACTL_WPSCNT_FOUR :
 320                        LPAIF_DMACTL_WPSCNT_EIGHT);
 321                        break;
 322                default:
 323                        dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
 324                                bitwidth, channels);
 325                        return -EINVAL;
 326                }
 327                break;
 328        default:
 329                dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
 330                        bitwidth, channels);
 331                return -EINVAL;
 332        }
 333
 334        ret = regmap_fields_write(dmactl->wpscnt, id, regval);
 335        if (ret) {
 336                dev_err(soc_runtime->dev, "error writing to dmactl reg: %d\n",
 337                        ret);
 338                return ret;
 339        }
 340
 341        return 0;
 342}
 343
 344static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component,
 345                                         struct snd_pcm_substream *substream)
 346{
 347        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 348        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 349        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 350        struct snd_pcm_runtime *rt = substream->runtime;
 351        struct lpass_pcm_data *pcm_data = rt->private_data;
 352        struct lpass_variant *v = drvdata->variant;
 353        unsigned int reg;
 354        int ret;
 355        struct regmap *map;
 356        unsigned int dai_id = cpu_dai->driver->id;
 357
 358        if (dai_id == LPASS_DP_RX)
 359                map = drvdata->hdmiif_map;
 360        else
 361                map = drvdata->lpaif_map;
 362
 363        reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id);
 364        ret = regmap_write(map, reg, 0);
 365        if (ret)
 366                dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
 367                        ret);
 368
 369        return ret;
 370}
 371
 372static int lpass_platform_pcmops_prepare(struct snd_soc_component *component,
 373                                         struct snd_pcm_substream *substream)
 374{
 375        struct snd_pcm_runtime *runtime = substream->runtime;
 376        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 377        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 378        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 379        struct snd_pcm_runtime *rt = substream->runtime;
 380        struct lpass_pcm_data *pcm_data = rt->private_data;
 381        struct lpass_variant *v = drvdata->variant;
 382        struct lpaif_dmactl *dmactl;
 383        struct regmap *map;
 384        int ret, id, ch, dir = substream->stream;
 385        unsigned int dai_id = cpu_dai->driver->id;
 386
 387
 388        ch = pcm_data->dma_ch;
 389        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK) {
 390                if (dai_id == LPASS_DP_RX) {
 391                        dmactl = drvdata->hdmi_rd_dmactl;
 392                        map = drvdata->hdmiif_map;
 393                } else {
 394                        dmactl = drvdata->rd_dmactl;
 395                        map = drvdata->lpaif_map;
 396                }
 397
 398                id = pcm_data->dma_ch;
 399        } else {
 400                dmactl = drvdata->wr_dmactl;
 401                id = pcm_data->dma_ch - v->wrdma_channel_start;
 402                map = drvdata->lpaif_map;
 403        }
 404
 405        ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id),
 406                                runtime->dma_addr);
 407        if (ret) {
 408                dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
 409                        ret);
 410                return ret;
 411        }
 412
 413        ret = regmap_write(map, LPAIF_DMABUFF_REG(v, ch, dir, dai_id),
 414                        (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 415        if (ret) {
 416                dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
 417                        ret);
 418                return ret;
 419        }
 420
 421        ret = regmap_write(map, LPAIF_DMAPER_REG(v, ch, dir, dai_id),
 422                        (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 423        if (ret) {
 424                dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
 425                        ret);
 426                return ret;
 427        }
 428
 429        ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON);
 430        if (ret) {
 431                dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
 432                        ret);
 433                return ret;
 434        }
 435
 436        return 0;
 437}
 438
 439static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
 440                                         struct snd_pcm_substream *substream,
 441                                         int cmd)
 442{
 443        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 444        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 445        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 446        struct snd_pcm_runtime *rt = substream->runtime;
 447        struct lpass_pcm_data *pcm_data = rt->private_data;
 448        struct lpass_variant *v = drvdata->variant;
 449        struct lpaif_dmactl *dmactl;
 450        struct regmap *map;
 451        int ret, ch, id;
 452        int dir = substream->stream;
 453        unsigned int reg_irqclr = 0, val_irqclr = 0;
 454        unsigned int  reg_irqen = 0, val_irqen = 0, val_mask = 0;
 455        unsigned int dai_id = cpu_dai->driver->id;
 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
 473        switch (cmd) {
 474        case SNDRV_PCM_TRIGGER_START:
 475        case SNDRV_PCM_TRIGGER_RESUME:
 476        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 477                ret = regmap_fields_write(dmactl->enable, id,
 478                                                 LPAIF_DMACTL_ENABLE_ON);
 479                if (ret) {
 480                        dev_err(soc_runtime->dev,
 481                                "error writing to rdmactl reg: %d\n", ret);
 482                        return ret;
 483                }
 484                switch (dai_id) {
 485                case LPASS_DP_RX:
 486                        ret = regmap_fields_write(dmactl->dyncclk, id,
 487                                         LPAIF_DMACTL_DYNCLK_ON);
 488                        if (ret) {
 489                                dev_err(soc_runtime->dev,
 490                                        "error writing to rdmactl reg: %d\n", ret);
 491                                return ret;
 492                        }
 493                        reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
 494                        val_irqclr = (LPAIF_IRQ_ALL(ch) |
 495                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 496                                        LPAIF_IRQ_HDMI_METADONE |
 497                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 498
 499                        reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
 500                        val_mask = (LPAIF_IRQ_ALL(ch) |
 501                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 502                                        LPAIF_IRQ_HDMI_METADONE |
 503                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 504                        val_irqen = (LPAIF_IRQ_ALL(ch) |
 505                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 506                                        LPAIF_IRQ_HDMI_METADONE |
 507                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 508                        break;
 509                case MI2S_PRIMARY:
 510                case MI2S_SECONDARY:
 511                case MI2S_TERTIARY:
 512                case MI2S_QUATERNARY:
 513                case MI2S_QUINARY:
 514                        reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 515                        val_irqclr = LPAIF_IRQ_ALL(ch);
 516
 517
 518                        reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
 519                        val_mask = LPAIF_IRQ_ALL(ch);
 520                        val_irqen = LPAIF_IRQ_ALL(ch);
 521                        break;
 522                default:
 523                        dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
 524                        return -EINVAL;
 525                }
 526
 527                ret = regmap_update_bits(map, reg_irqclr, val_irqclr, val_irqclr);
 528                if (ret) {
 529                        dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", ret);
 530                        return ret;
 531                }
 532                ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen);
 533                if (ret) {
 534                        dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret);
 535                        return ret;
 536                }
 537                break;
 538        case SNDRV_PCM_TRIGGER_STOP:
 539        case SNDRV_PCM_TRIGGER_SUSPEND:
 540        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 541                ret = regmap_fields_write(dmactl->enable, id,
 542                                         LPAIF_DMACTL_ENABLE_OFF);
 543                if (ret) {
 544                        dev_err(soc_runtime->dev,
 545                                "error writing to rdmactl reg: %d\n", ret);
 546                        return ret;
 547                }
 548                switch (dai_id) {
 549                case LPASS_DP_RX:
 550                        ret = regmap_fields_write(dmactl->dyncclk, id,
 551                                         LPAIF_DMACTL_DYNCLK_OFF);
 552                        if (ret) {
 553                                dev_err(soc_runtime->dev,
 554                                        "error writing to rdmactl reg: %d\n", ret);
 555                                return ret;
 556                        }
 557                        reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
 558                        val_mask = (LPAIF_IRQ_ALL(ch) |
 559                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
 560                                        LPAIF_IRQ_HDMI_METADONE |
 561                                        LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
 562                        val_irqen = 0;
 563                        break;
 564                case MI2S_PRIMARY:
 565                case MI2S_SECONDARY:
 566                case MI2S_TERTIARY:
 567                case MI2S_QUATERNARY:
 568                case MI2S_QUINARY:
 569                        reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
 570                        val_mask = LPAIF_IRQ_ALL(ch);
 571                        val_irqen = 0;
 572                        break;
 573                default:
 574                        dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
 575                        return -EINVAL;
 576                }
 577
 578                ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen);
 579                if (ret) {
 580                        dev_err(soc_runtime->dev,
 581                                "error writing to irqen reg: %d\n", ret);
 582                        return ret;
 583                }
 584                break;
 585        }
 586
 587        return 0;
 588}
 589
 590static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 591                struct snd_soc_component *component,
 592                struct snd_pcm_substream *substream)
 593{
 594        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 595        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 596        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 597        struct snd_pcm_runtime *rt = substream->runtime;
 598        struct lpass_pcm_data *pcm_data = rt->private_data;
 599        struct lpass_variant *v = drvdata->variant;
 600        unsigned int base_addr, curr_addr;
 601        int ret, ch, dir = substream->stream;
 602        struct regmap *map;
 603        unsigned int dai_id = cpu_dai->driver->id;
 604
 605        if (dai_id == LPASS_DP_RX)
 606                map = drvdata->hdmiif_map;
 607        else
 608                map = drvdata->lpaif_map;
 609
 610        ch = pcm_data->dma_ch;
 611
 612        ret = regmap_read(map,
 613                        LPAIF_DMABASE_REG(v, ch, dir, dai_id), &base_addr);
 614        if (ret) {
 615                dev_err(soc_runtime->dev,
 616                        "error reading from rdmabase reg: %d\n", ret);
 617                return ret;
 618        }
 619
 620        ret = regmap_read(map,
 621                        LPAIF_DMACURR_REG(v, ch, dir, dai_id), &curr_addr);
 622        if (ret) {
 623                dev_err(soc_runtime->dev,
 624                        "error reading from rdmacurr reg: %d\n", ret);
 625                return ret;
 626        }
 627
 628        return bytes_to_frames(substream->runtime, curr_addr - base_addr);
 629}
 630
 631static irqreturn_t lpass_dma_interrupt_handler(
 632                        struct snd_pcm_substream *substream,
 633                        struct lpass_data *drvdata,
 634                        int chan, u32 interrupts)
 635{
 636        struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
 637        struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
 638        struct lpass_variant *v = drvdata->variant;
 639        irqreturn_t ret = IRQ_NONE;
 640        int rv;
 641        unsigned int reg, val, mask;
 642        struct regmap *map;
 643        unsigned int dai_id = cpu_dai->driver->id;
 644
 645        mask = LPAIF_IRQ_ALL(chan);
 646        switch (dai_id) {
 647        case LPASS_DP_RX:
 648                map = drvdata->hdmiif_map;
 649                reg = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
 650                val = (LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
 651                LPAIF_IRQ_HDMI_METADONE |
 652                LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan));
 653        break;
 654        case MI2S_PRIMARY:
 655        case MI2S_SECONDARY:
 656        case MI2S_TERTIARY:
 657        case MI2S_QUATERNARY:
 658        case MI2S_QUINARY:
 659                map = drvdata->lpaif_map;
 660                reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 661                val = 0;
 662        break;
 663        default:
 664        dev_err(soc_runtime->dev, "%s: invalid  %d interface\n", __func__, dai_id);
 665        return -EINVAL;
 666        }
 667        if (interrupts & LPAIF_IRQ_PER(chan)) {
 668                rv = regmap_update_bits(map, reg, mask, (LPAIF_IRQ_PER(chan) | val));
 669                if (rv) {
 670                        dev_err(soc_runtime->dev,
 671                                "error writing to irqclear reg: %d\n", rv);
 672                        return IRQ_NONE;
 673                }
 674                snd_pcm_period_elapsed(substream);
 675                ret = IRQ_HANDLED;
 676        }
 677
 678        if (interrupts & LPAIF_IRQ_XRUN(chan)) {
 679                rv = regmap_update_bits(map, reg, mask, (LPAIF_IRQ_XRUN(chan) | val));
 680                if (rv) {
 681                        dev_err(soc_runtime->dev,
 682                                "error writing to irqclear reg: %d\n", rv);
 683                        return IRQ_NONE;
 684                }
 685                dev_warn(soc_runtime->dev, "xrun warning\n");
 686                snd_pcm_stop_xrun(substream);
 687                ret = IRQ_HANDLED;
 688        }
 689
 690        if (interrupts & LPAIF_IRQ_ERR(chan)) {
 691                rv = regmap_update_bits(map, reg, mask, (LPAIF_IRQ_ERR(chan) | val));
 692                if (rv) {
 693                        dev_err(soc_runtime->dev,
 694                                "error writing to irqclear reg: %d\n", rv);
 695                        return IRQ_NONE;
 696                }
 697                dev_err(soc_runtime->dev, "bus access error\n");
 698                snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
 699                ret = IRQ_HANDLED;
 700        }
 701
 702        if (interrupts & val) {
 703                rv = regmap_write(map, reg, val);
 704                if (rv) {
 705                        dev_err(soc_runtime->dev,
 706                        "error writing to irqclear reg: %d\n", rv);
 707                        return IRQ_NONE;
 708                }
 709                ret = IRQ_HANDLED;
 710        }
 711
 712        return ret;
 713}
 714
 715static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 716{
 717        struct lpass_data *drvdata = data;
 718        struct lpass_variant *v = drvdata->variant;
 719        unsigned int irqs;
 720        int rv, chan;
 721
 722        rv = regmap_read(drvdata->lpaif_map,
 723                        LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
 724        if (rv) {
 725                pr_err("error reading from irqstat reg: %d\n", rv);
 726                return IRQ_NONE;
 727        }
 728
 729        /* Handle per channel interrupts */
 730        for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
 731                if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
 732                        rv = lpass_dma_interrupt_handler(
 733                                                drvdata->substream[chan],
 734                                                drvdata, chan, irqs);
 735                        if (rv != IRQ_HANDLED)
 736                                return rv;
 737                }
 738        }
 739
 740        return IRQ_HANDLED;
 741}
 742
 743static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data)
 744{
 745        struct lpass_data *drvdata = data;
 746        struct lpass_variant *v = drvdata->variant;
 747        unsigned int irqs;
 748        int rv, chan;
 749
 750        rv = regmap_read(drvdata->hdmiif_map,
 751                        LPASS_HDMITX_APP_IRQSTAT_REG(v), &irqs);
 752        if (rv) {
 753                pr_err("error reading from irqstat reg: %d\n", rv);
 754                return IRQ_NONE;
 755        }
 756
 757        /* Handle per channel interrupts */
 758        for (chan = 0; chan < LPASS_MAX_HDMI_DMA_CHANNELS; chan++) {
 759                if (irqs & (LPAIF_IRQ_ALL(chan) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
 760                                LPAIF_IRQ_HDMI_METADONE |
 761                                LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan))
 762                        && drvdata->hdmi_substream[chan]) {
 763                        rv = lpass_dma_interrupt_handler(
 764                                                drvdata->hdmi_substream[chan],
 765                                                drvdata, chan, irqs);
 766                        if (rv != IRQ_HANDLED)
 767                                return rv;
 768                }
 769        }
 770
 771        return IRQ_HANDLED;
 772}
 773
 774static int lpass_platform_pcm_new(struct snd_soc_component *component,
 775                                  struct snd_soc_pcm_runtime *soc_runtime)
 776{
 777        struct snd_pcm *pcm = soc_runtime->pcm;
 778        size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
 779
 780        return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
 781                                            component->dev, size);
 782}
 783
 784static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
 785{
 786        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 787        struct regmap *map;
 788        unsigned int dai_id = component->id;
 789
 790        if (dai_id == LPASS_DP_RX)
 791                map = drvdata->hdmiif_map;
 792        else
 793                map = drvdata->lpaif_map;
 794
 795        regcache_cache_only(map, true);
 796        regcache_mark_dirty(map);
 797
 798        return 0;
 799}
 800
 801static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
 802{
 803        struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 804        struct regmap *map;
 805        unsigned int dai_id = component->id;
 806
 807        if (dai_id == LPASS_DP_RX)
 808                map = drvdata->hdmiif_map;
 809        else
 810                map = drvdata->lpaif_map;
 811
 812        regcache_cache_only(map, false);
 813        return regcache_sync(map);
 814}
 815
 816
 817static const struct snd_soc_component_driver lpass_component_driver = {
 818        .name           = DRV_NAME,
 819        .open           = lpass_platform_pcmops_open,
 820        .close          = lpass_platform_pcmops_close,
 821        .hw_params      = lpass_platform_pcmops_hw_params,
 822        .hw_free        = lpass_platform_pcmops_hw_free,
 823        .prepare        = lpass_platform_pcmops_prepare,
 824        .trigger        = lpass_platform_pcmops_trigger,
 825        .pointer        = lpass_platform_pcmops_pointer,
 826        .pcm_construct  = lpass_platform_pcm_new,
 827        .suspend                = lpass_platform_pcmops_suspend,
 828        .resume                 = lpass_platform_pcmops_resume,
 829
 830};
 831
 832int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 833{
 834        struct lpass_data *drvdata = platform_get_drvdata(pdev);
 835        struct lpass_variant *v = drvdata->variant;
 836        int ret;
 837
 838        drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 839        if (drvdata->lpaif_irq < 0)
 840                return -ENODEV;
 841
 842        /* ensure audio hardware is disabled */
 843        ret = regmap_write(drvdata->lpaif_map,
 844                        LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
 845        if (ret) {
 846                dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
 847                return ret;
 848        }
 849
 850        ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
 851                        lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
 852                        "lpass-irq-lpaif", drvdata);
 853        if (ret) {
 854                dev_err(&pdev->dev, "irq request failed: %d\n", ret);
 855                return ret;
 856        }
 857
 858        ret = lpass_platform_alloc_dmactl_fields(&pdev->dev,
 859                                                 drvdata->lpaif_map);
 860        if (ret) {
 861                dev_err(&pdev->dev,
 862                        "error initializing dmactl fields: %d\n", ret);
 863                return ret;
 864        }
 865
 866        if (drvdata->hdmi_port_enable) {
 867                drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi");
 868                if (drvdata->hdmiif_irq < 0)
 869                        return -ENODEV;
 870
 871                ret = devm_request_irq(&pdev->dev, drvdata->hdmiif_irq,
 872                                lpass_platform_hdmiif_irq, 0, "lpass-irq-hdmi", drvdata);
 873                if (ret) {
 874                        dev_err(&pdev->dev, "irq hdmi request failed: %d\n", ret);
 875                        return ret;
 876                }
 877                ret = regmap_write(drvdata->hdmiif_map,
 878                                LPASS_HDMITX_APP_IRQEN_REG(v), 0);
 879                if (ret) {
 880                        dev_err(&pdev->dev, "error writing to hdmi irqen reg: %d\n", ret);
 881                        return ret;
 882                }
 883
 884                ret = lpass_platform_alloc_hdmidmactl_fields(&pdev->dev,
 885                                                         drvdata->hdmiif_map);
 886                if (ret) {
 887                        dev_err(&pdev->dev,
 888                                "error initializing hdmidmactl fields: %d\n", ret);
 889                        return ret;
 890                }
 891        }
 892        return devm_snd_soc_register_component(&pdev->dev,
 893                        &lpass_component_driver, NULL, 0);
 894}
 895EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
 896
 897MODULE_DESCRIPTION("QTi LPASS Platform Driver");
 898MODULE_LICENSE("GPL v2");
 899