Merge tag 'mac80211-for-davem-2016-06-29-v2' of git://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / sound / soc / sti / sti_uniperif.c
index 39bcefe..488ef4e 100644 (file)
 
 #include "uniperif.h"
 
+/*
+ * User frame size shall be 2, 4, 6 or 8 32-bits words length
+ * (i.e. 8, 16, 24 or 32 bytes)
+ * This constraint comes from allowed values for
+ * UNIPERIF_I2S_FMT_NUM_CH register
+ */
+#define UNIPERIF_MAX_FRAME_SZ 0x20
+#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
+
+int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                              unsigned int rx_mask, int slots,
+                              int slot_width)
+{
+       struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+       struct uniperif *uni = priv->dai_data.uni;
+       int i, frame_size, avail_slots;
+
+       if (!UNIPERIF_TYPE_IS_TDM(uni)) {
+               dev_err(uni->dev, "cpu dai not in tdm mode\n");
+               return -EINVAL;
+       }
+
+       /* store info in unip context */
+       uni->tdm_slot.slots = slots;
+       uni->tdm_slot.slot_width = slot_width;
+       /* unip is unidirectionnal */
+       uni->tdm_slot.mask = (tx_mask != 0) ? tx_mask : rx_mask;
+
+       /* number of available timeslots */
+       for (i = 0, avail_slots = 0; i < uni->tdm_slot.slots; i++) {
+               if ((uni->tdm_slot.mask >> i) & 0x01)
+                       avail_slots++;
+       }
+       uni->tdm_slot.avail_slots = avail_slots;
+
+       /* frame size in bytes */
+       frame_size = uni->tdm_slot.avail_slots * uni->tdm_slot.slot_width / 8;
+
+       /* check frame size is allowed */
+       if ((frame_size > UNIPERIF_MAX_FRAME_SZ) ||
+           (frame_size & ~(int)UNIPERIF_ALLOWED_FRAME_SZ)) {
+               dev_err(uni->dev, "frame size not allowed: %d bytes\n",
+                       frame_size);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
+                              struct snd_pcm_hw_rule *rule)
+{
+       struct uniperif *uni = rule->private;
+       struct snd_interval t;
+
+       t.min = uni->tdm_slot.avail_slots;
+       t.max = uni->tdm_slot.avail_slots;
+       t.openmin = 0;
+       t.openmax = 0;
+       t.integer = 0;
+
+       return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
+                                struct snd_pcm_hw_rule *rule)
+{
+       struct uniperif *uni = rule->private;
+       struct snd_mask *maskp = hw_param_mask(params, rule->var);
+       u64 format;
+
+       switch (uni->tdm_slot.slot_width) {
+       case 16:
+               format = SNDRV_PCM_FMTBIT_S16_LE;
+               break;
+       case 32:
+               format = SNDRV_PCM_FMTBIT_S32_LE;
+               break;
+       default:
+               dev_err(uni->dev, "format not supported: %d bits\n",
+                       uni->tdm_slot.slot_width);
+               return -EINVAL;
+       }
+
+       maskp->bits[0] &= (u_int32_t)format;
+       maskp->bits[1] &= (u_int32_t)(format >> 32);
+       /* clear remaining indexes */
+       memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX - 64) / 8);
+
+       if (!maskp->bits[0] && !maskp->bits[1])
+               return -EINVAL;
+
+       return 0;
+}
+
+int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
+                                  unsigned int *word_pos)
+{
+       int slot_width = uni->tdm_slot.slot_width / 8;
+       int slots_num = uni->tdm_slot.slots;
+       unsigned int slots_mask = uni->tdm_slot.mask;
+       int i, j, k;
+       unsigned int word16_pos[4];
+
+       /* word16_pos:
+        * word16_pos[0] = WORDX_LSB
+        * word16_pos[1] = WORDX_MSB,
+        * word16_pos[2] = WORDX+1_LSB
+        * word16_pos[3] = WORDX+1_MSB
+        */
+
+       /* set unip word position */
+       for (i = 0, j = 0, k = 0; (i < slots_num) && (k < WORD_MAX); i++) {
+               if ((slots_mask >> i) & 0x01) {
+                       word16_pos[j] = i * slot_width;
+
+                       if (slot_width == 4) {
+                               word16_pos[j + 1] = word16_pos[j] + 2;
+                               j++;
+                       }
+                       j++;
+
+                       if (j > 3) {
+                               word_pos[k] = word16_pos[1] |
+                                             (word16_pos[0] << 8) |
+                                             (word16_pos[3] << 16) |
+                                             (word16_pos[2] << 24);
+                               j = 0;
+                               k++;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 /*
  * sti_uniperiph_dai_create_ctrl
  * This function is used to create Ctrl associated to DAI but also pcm device.
@@ -45,10 +181,16 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
 {
+       struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+       struct uniperif *uni = priv->dai_data.uni;
        struct snd_dmaengine_dai_dma_data *dma_data;
        int transfer_size;
 
-       transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
+       if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM)
+               /* transfer size = user frame size (in 32-bits FIFO cell) */
+               transfer_size = snd_soc_params_to_frame_size(params) / 32;
+       else
+               transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
 
        dma_data = snd_soc_dai_get_dma_data(dai, substream);
        dma_data->maxburst = transfer_size;