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