linux/sound/soc/sti/sti_uniperif.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) STMicroelectronics SA 2015
   4 * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
   5 *          for STMicroelectronics.
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/pinctrl/consumer.h>
  10#include <linux/delay.h>
  11
  12#include "uniperif.h"
  13
  14/*
  15 * User frame size shall be 2, 4, 6 or 8 32-bits words length
  16 * (i.e. 8, 16, 24 or 32 bytes)
  17 * This constraint comes from allowed values for
  18 * UNIPERIF_I2S_FMT_NUM_CH register
  19 */
  20#define UNIPERIF_MAX_FRAME_SZ 0x20
  21#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
  22
  23struct sti_uniperiph_dev_data {
  24        unsigned int id; /* Nb available player instances */
  25        unsigned int version; /* player IP version */
  26        unsigned int stream;
  27        const char *dai_names;
  28        enum uniperif_type type;
  29};
  30
  31static const struct sti_uniperiph_dev_data sti_uniplayer_hdmi = {
  32        .id = 0,
  33        .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
  34        .stream = SNDRV_PCM_STREAM_PLAYBACK,
  35        .dai_names = "Uni Player #0 (HDMI)",
  36        .type = SND_ST_UNIPERIF_TYPE_HDMI
  37};
  38
  39static const struct sti_uniperiph_dev_data sti_uniplayer_pcm_out = {
  40        .id = 1,
  41        .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
  42        .stream = SNDRV_PCM_STREAM_PLAYBACK,
  43        .dai_names = "Uni Player #1 (PCM OUT)",
  44        .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
  45};
  46
  47static const struct sti_uniperiph_dev_data sti_uniplayer_dac = {
  48        .id = 2,
  49        .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
  50        .stream = SNDRV_PCM_STREAM_PLAYBACK,
  51        .dai_names = "Uni Player #2 (DAC)",
  52        .type = SND_ST_UNIPERIF_TYPE_PCM,
  53};
  54
  55static const struct sti_uniperiph_dev_data sti_uniplayer_spdif = {
  56        .id = 3,
  57        .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
  58        .stream = SNDRV_PCM_STREAM_PLAYBACK,
  59        .dai_names = "Uni Player #3 (SPDIF)",
  60        .type = SND_ST_UNIPERIF_TYPE_SPDIF
  61};
  62
  63static const struct sti_uniperiph_dev_data sti_unireader_pcm_in = {
  64        .id = 0,
  65        .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
  66        .stream = SNDRV_PCM_STREAM_CAPTURE,
  67        .dai_names = "Uni Reader #0 (PCM IN)",
  68        .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
  69};
  70
  71static const struct sti_uniperiph_dev_data sti_unireader_hdmi_in = {
  72        .id = 1,
  73        .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
  74        .stream = SNDRV_PCM_STREAM_CAPTURE,
  75        .dai_names = "Uni Reader #1 (HDMI IN)",
  76        .type = SND_ST_UNIPERIF_TYPE_PCM,
  77};
  78
  79static const struct of_device_id snd_soc_sti_match[] = {
  80        { .compatible = "st,stih407-uni-player-hdmi",
  81          .data = &sti_uniplayer_hdmi
  82        },
  83        { .compatible = "st,stih407-uni-player-pcm-out",
  84          .data = &sti_uniplayer_pcm_out
  85        },
  86        { .compatible = "st,stih407-uni-player-dac",
  87          .data = &sti_uniplayer_dac
  88        },
  89        { .compatible = "st,stih407-uni-player-spdif",
  90          .data = &sti_uniplayer_spdif
  91        },
  92        { .compatible = "st,stih407-uni-reader-pcm_in",
  93          .data = &sti_unireader_pcm_in
  94        },
  95        { .compatible = "st,stih407-uni-reader-hdmi",
  96          .data = &sti_unireader_hdmi_in
  97        },
  98        {},
  99};
 100MODULE_DEVICE_TABLE(of, snd_soc_sti_match);
 101
 102int  sti_uniperiph_reset(struct uniperif *uni)
 103{
 104        int count = 10;
 105
 106        /* Reset uniperipheral uni */
 107        SET_UNIPERIF_SOFT_RST_SOFT_RST(uni);
 108
 109        if (uni->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
 110                while (GET_UNIPERIF_SOFT_RST_SOFT_RST(uni) && count) {
 111                        udelay(5);
 112                        count--;
 113                }
 114        }
 115
 116        if (!count) {
 117                dev_err(uni->dev, "Failed to reset uniperif\n");
 118                return -EIO;
 119        }
 120
 121        return 0;
 122}
 123
 124int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 125                               unsigned int rx_mask, int slots,
 126                               int slot_width)
 127{
 128        struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
 129        struct uniperif *uni = priv->dai_data.uni;
 130        int i, frame_size, avail_slots;
 131
 132        if (!UNIPERIF_TYPE_IS_TDM(uni)) {
 133                dev_err(uni->dev, "cpu dai not in tdm mode\n");
 134                return -EINVAL;
 135        }
 136
 137        /* store info in unip context */
 138        uni->tdm_slot.slots = slots;
 139        uni->tdm_slot.slot_width = slot_width;
 140        /* unip is unidirectionnal */
 141        uni->tdm_slot.mask = (tx_mask != 0) ? tx_mask : rx_mask;
 142
 143        /* number of available timeslots */
 144        for (i = 0, avail_slots = 0; i < uni->tdm_slot.slots; i++) {
 145                if ((uni->tdm_slot.mask >> i) & 0x01)
 146                        avail_slots++;
 147        }
 148        uni->tdm_slot.avail_slots = avail_slots;
 149
 150        /* frame size in bytes */
 151        frame_size = uni->tdm_slot.avail_slots * uni->tdm_slot.slot_width / 8;
 152
 153        /* check frame size is allowed */
 154        if ((frame_size > UNIPERIF_MAX_FRAME_SZ) ||
 155            (frame_size & ~(int)UNIPERIF_ALLOWED_FRAME_SZ)) {
 156                dev_err(uni->dev, "frame size not allowed: %d bytes\n",
 157                        frame_size);
 158                return -EINVAL;
 159        }
 160
 161        return 0;
 162}
 163
 164int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
 165                               struct snd_pcm_hw_rule *rule)
 166{
 167        struct uniperif *uni = rule->private;
 168        struct snd_interval t;
 169
 170        t.min = uni->tdm_slot.avail_slots;
 171        t.max = uni->tdm_slot.avail_slots;
 172        t.openmin = 0;
 173        t.openmax = 0;
 174        t.integer = 0;
 175
 176        return snd_interval_refine(hw_param_interval(params, rule->var), &t);
 177}
 178
 179int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
 180                                 struct snd_pcm_hw_rule *rule)
 181{
 182        struct uniperif *uni = rule->private;
 183        struct snd_mask *maskp = hw_param_mask(params, rule->var);
 184        u64 format;
 185
 186        switch (uni->tdm_slot.slot_width) {
 187        case 16:
 188                format = SNDRV_PCM_FMTBIT_S16_LE;
 189                break;
 190        case 32:
 191                format = SNDRV_PCM_FMTBIT_S32_LE;
 192                break;
 193        default:
 194                dev_err(uni->dev, "format not supported: %d bits\n",
 195                        uni->tdm_slot.slot_width);
 196                return -EINVAL;
 197        }
 198
 199        maskp->bits[0] &= (u_int32_t)format;
 200        maskp->bits[1] &= (u_int32_t)(format >> 32);
 201        /* clear remaining indexes */
 202        memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX - 64) / 8);
 203
 204        if (!maskp->bits[0] && !maskp->bits[1])
 205                return -EINVAL;
 206
 207        return 0;
 208}
 209
 210int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
 211                                   unsigned int *word_pos)
 212{
 213        int slot_width = uni->tdm_slot.slot_width / 8;
 214        int slots_num = uni->tdm_slot.slots;
 215        unsigned int slots_mask = uni->tdm_slot.mask;
 216        int i, j, k;
 217        unsigned int word16_pos[4];
 218
 219        /* word16_pos:
 220         * word16_pos[0] = WORDX_LSB
 221         * word16_pos[1] = WORDX_MSB,
 222         * word16_pos[2] = WORDX+1_LSB
 223         * word16_pos[3] = WORDX+1_MSB
 224         */
 225
 226        /* set unip word position */
 227        for (i = 0, j = 0, k = 0; (i < slots_num) && (k < WORD_MAX); i++) {
 228                if ((slots_mask >> i) & 0x01) {
 229                        word16_pos[j] = i * slot_width;
 230
 231                        if (slot_width == 4) {
 232                                word16_pos[j + 1] = word16_pos[j] + 2;
 233                                j++;
 234                        }
 235                        j++;
 236
 237                        if (j > 3) {
 238                                word_pos[k] = word16_pos[1] |
 239                                              (word16_pos[0] << 8) |
 240                                              (word16_pos[3] << 16) |
 241                                              (word16_pos[2] << 24);
 242                                j = 0;
 243                                k++;
 244                        }
 245                }
 246        }
 247
 248        return 0;
 249}
 250
 251/*
 252 * sti_uniperiph_dai_create_ctrl
 253 * This function is used to create Ctrl associated to DAI but also pcm device.
 254 * Request is done by front end to associate ctrl with pcm device id
 255 */
 256static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
 257{
 258        struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
 259        struct uniperif *uni = priv->dai_data.uni;
 260        struct snd_kcontrol_new *ctrl;
 261        int i;
 262
 263        if (!uni->num_ctrls)
 264                return 0;
 265
 266        for (i = 0; i < uni->num_ctrls; i++) {
 267                /*
 268                 * Several Control can have same name. Controls are indexed on
 269                 * Uniperipheral instance ID
 270                 */
 271                ctrl = &uni->snd_ctrls[i];
 272                ctrl->index = uni->id;
 273                ctrl->device = uni->id;
 274        }
 275
 276        return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
 277}
 278
 279/*
 280 * DAI
 281 */
 282int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
 283                                struct snd_pcm_hw_params *params,
 284                                struct snd_soc_dai *dai)
 285{
 286        struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
 287        struct uniperif *uni = priv->dai_data.uni;
 288        struct snd_dmaengine_dai_dma_data *dma_data;
 289        int transfer_size;
 290
 291        if (uni->type == SND_ST_UNIPERIF_TYPE_TDM)
 292                /* transfer size = user frame size (in 32-bits FIFO cell) */
 293                transfer_size = snd_soc_params_to_frame_size(params) / 32;
 294        else
 295                transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
 296
 297        dma_data = snd_soc_dai_get_dma_data(dai, substream);
 298        dma_data->maxburst = transfer_size;
 299
 300        return 0;
 301}
 302
 303int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 304{
 305        struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
 306
 307        priv->dai_data.uni->daifmt = fmt;
 308
 309        return 0;
 310}
 311
 312static int sti_uniperiph_suspend(struct snd_soc_component *component)
 313{
 314        struct sti_uniperiph_data *priv = snd_soc_component_get_drvdata(component);
 315        struct uniperif *uni = priv->dai_data.uni;
 316        int ret;
 317
 318        /* The uniperipheral should be in stopped state */
 319        if (uni->state != UNIPERIF_STATE_STOPPED) {
 320                dev_err(uni->dev, "%s: invalid uni state( %d)\n",
 321                        __func__, (int)uni->state);
 322                return -EBUSY;
 323        }
 324
 325        /* Pinctrl: switch pinstate to sleep */
 326        ret = pinctrl_pm_select_sleep_state(uni->dev);
 327        if (ret)
 328                dev_err(uni->dev, "%s: failed to select pinctrl state\n",
 329                        __func__);
 330
 331        return ret;
 332}
 333
 334static int sti_uniperiph_resume(struct snd_soc_component *component)
 335{
 336        struct sti_uniperiph_data *priv = snd_soc_component_get_drvdata(component);
 337        struct uniperif *uni = priv->dai_data.uni;
 338        int ret;
 339
 340        if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
 341                ret = uni_player_resume(uni);
 342                if (ret)
 343                        return ret;
 344        }
 345
 346        /* pinctrl: switch pinstate to default */
 347        ret = pinctrl_pm_select_default_state(uni->dev);
 348        if (ret)
 349                dev_err(uni->dev, "%s: failed to select pinctrl state\n",
 350                        __func__);
 351
 352        return ret;
 353}
 354
 355static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
 356{
 357        struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
 358        struct sti_uniperiph_dai *dai_data = &priv->dai_data;
 359
 360        /* DMA settings*/
 361        if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK)
 362                snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
 363        else
 364                snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
 365
 366        dai_data->dma_data.addr = dai_data->uni->fifo_phys_address;
 367        dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 368
 369        return sti_uniperiph_dai_create_ctrl(dai);
 370}
 371
 372static const struct snd_soc_dai_driver sti_uniperiph_dai_template = {
 373        .probe = sti_uniperiph_dai_probe,
 374};
 375
 376static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
 377        .name = "sti_cpu_dai",
 378        .suspend = sti_uniperiph_suspend,
 379        .resume = sti_uniperiph_resume
 380};
 381
 382static int sti_uniperiph_cpu_dai_of(struct device_node *node,
 383                                    struct sti_uniperiph_data *priv)
 384{
 385        struct device *dev = &priv->pdev->dev;
 386        struct sti_uniperiph_dai *dai_data = &priv->dai_data;
 387        struct snd_soc_dai_driver *dai = priv->dai;
 388        struct snd_soc_pcm_stream *stream;
 389        struct uniperif *uni;
 390        const struct of_device_id *of_id;
 391        const struct sti_uniperiph_dev_data *dev_data;
 392        const char *mode;
 393        int ret;
 394
 395        /* Populate data structure depending on compatibility */
 396        of_id = of_match_node(snd_soc_sti_match, node);
 397        if (!of_id->data) {
 398                dev_err(dev, "data associated to device is missing\n");
 399                return -EINVAL;
 400        }
 401        dev_data = (struct sti_uniperiph_dev_data *)of_id->data;
 402
 403        uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
 404        if (!uni)
 405                return -ENOMEM;
 406
 407        uni->id = dev_data->id;
 408        uni->ver = dev_data->version;
 409
 410        *dai = sti_uniperiph_dai_template;
 411        dai->name = dev_data->dai_names;
 412
 413        /* Get resources and base address */
 414        uni->base = devm_platform_get_and_ioremap_resource(priv->pdev, 0, &uni->mem_region);
 415        if (IS_ERR(uni->base))
 416                return PTR_ERR(uni->base);
 417
 418        uni->fifo_phys_address = uni->mem_region->start +
 419                                     UNIPERIF_FIFO_DATA_OFFSET(uni);
 420
 421        uni->irq = platform_get_irq(priv->pdev, 0);
 422        if (uni->irq < 0)
 423                return -ENXIO;
 424
 425        uni->type = dev_data->type;
 426
 427        /* check if player should be configured for tdm */
 428        if (dev_data->type & SND_ST_UNIPERIF_TYPE_TDM) {
 429                if (!of_property_read_string(node, "st,tdm-mode", &mode))
 430                        uni->type = SND_ST_UNIPERIF_TYPE_TDM;
 431                else
 432                        uni->type = SND_ST_UNIPERIF_TYPE_PCM;
 433        }
 434
 435        dai_data->uni = uni;
 436        dai_data->stream = dev_data->stream;
 437
 438        if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
 439                ret = uni_player_init(priv->pdev, uni);
 440                stream = &dai->playback;
 441        } else {
 442                ret = uni_reader_init(priv->pdev, uni);
 443                stream = &dai->capture;
 444        }
 445        if (ret < 0)
 446                return ret;
 447
 448        dai->ops = uni->dai_ops;
 449
 450        stream->stream_name = dai->name;
 451        stream->channels_min = uni->hw->channels_min;
 452        stream->channels_max = uni->hw->channels_max;
 453        stream->rates = uni->hw->rates;
 454        stream->formats = uni->hw->formats;
 455
 456        return 0;
 457}
 458
 459static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
 460        .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
 461};
 462
 463static int sti_uniperiph_probe(struct platform_device *pdev)
 464{
 465        struct sti_uniperiph_data *priv;
 466        struct device_node *node = pdev->dev.of_node;
 467        int ret;
 468
 469        /* Allocate the private data and the CPU_DAI array */
 470        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 471        if (!priv)
 472                return -ENOMEM;
 473        priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL);
 474        if (!priv->dai)
 475                return -ENOMEM;
 476
 477        priv->pdev = pdev;
 478
 479        ret = sti_uniperiph_cpu_dai_of(node, priv);
 480        if (ret < 0)
 481                return ret;
 482
 483        dev_set_drvdata(&pdev->dev, priv);
 484
 485        ret = devm_snd_soc_register_component(&pdev->dev,
 486                                              &sti_uniperiph_dai_component,
 487                                              priv->dai, 1);
 488        if (ret < 0)
 489                return ret;
 490
 491        return devm_snd_dmaengine_pcm_register(&pdev->dev,
 492                                               &dmaengine_pcm_config, 0);
 493}
 494
 495static struct platform_driver sti_uniperiph_driver = {
 496        .driver = {
 497                .name = "sti-uniperiph-dai",
 498                .of_match_table = snd_soc_sti_match,
 499        },
 500        .probe = sti_uniperiph_probe,
 501};
 502module_platform_driver(sti_uniperiph_driver);
 503
 504MODULE_DESCRIPTION("uniperipheral DAI driver");
 505MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
 506MODULE_LICENSE("GPL v2");
 507