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