Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2020 BayLibre, SAS.
0004 // Author: Jerome Brunet <jbrunet@baylibre.com>
0005 
0006 #include <linux/clk.h>
0007 #include <sound/pcm_params.h>
0008 #include <sound/soc.h>
0009 #include <sound/soc-dai.h>
0010 
0011 #include "aiu.h"
0012 #include "aiu-fifo.h"
0013 
0014 #define AIU_IEC958_DCU_FF_CTRL_EN       BIT(0)
0015 #define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1)
0016 #define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE     GENMASK(3, 2)
0017 #define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD  BIT(2)
0018 #define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ   BIT(3)
0019 #define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4)
0020 #define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK    BIT(5)
0021 #define AIU_IEC958_DCU_FF_CTRL_CONTINUE     BIT(6)
0022 #define AIU_MEM_IEC958_CONTROL_ENDIAN       GENMASK(5, 3)
0023 #define AIU_MEM_IEC958_CONTROL_RD_DDR       BIT(6)
0024 #define AIU_MEM_IEC958_CONTROL_MODE_16BIT   BIT(7)
0025 #define AIU_MEM_IEC958_CONTROL_MODE_LINEAR  BIT(8)
0026 #define AIU_MEM_IEC958_BUF_CNTL_INIT        BIT(0)
0027 
0028 #define AIU_FIFO_SPDIF_BLOCK            8
0029 
0030 static struct snd_pcm_hardware fifo_spdif_pcm = {
0031     .info = (SNDRV_PCM_INFO_INTERLEAVED |
0032          SNDRV_PCM_INFO_MMAP |
0033          SNDRV_PCM_INFO_MMAP_VALID |
0034          SNDRV_PCM_INFO_PAUSE),
0035     .formats = AIU_FORMATS,
0036     .rate_min = 5512,
0037     .rate_max = 192000,
0038     .channels_min = 2,
0039     .channels_max = 2,
0040     .period_bytes_min = AIU_FIFO_SPDIF_BLOCK,
0041     .period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX,
0042     .periods_min = 2,
0043     .periods_max = UINT_MAX,
0044 
0045     /* No real justification for this */
0046     .buffer_bytes_max = 1 * 1024 * 1024,
0047 };
0048 
0049 static void fifo_spdif_dcu_enable(struct snd_soc_component *component,
0050                   bool enable)
0051 {
0052     snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
0053                       AIU_IEC958_DCU_FF_CTRL_EN,
0054                       enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0);
0055 }
0056 
0057 static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
0058                   struct snd_soc_dai *dai)
0059 {
0060     struct snd_soc_component *component = dai->component;
0061     int ret;
0062 
0063     ret = aiu_fifo_trigger(substream, cmd, dai);
0064     if (ret)
0065         return ret;
0066 
0067     switch (cmd) {
0068     case SNDRV_PCM_TRIGGER_START:
0069     case SNDRV_PCM_TRIGGER_RESUME:
0070     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0071         fifo_spdif_dcu_enable(component, true);
0072         break;
0073     case SNDRV_PCM_TRIGGER_SUSPEND:
0074     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0075     case SNDRV_PCM_TRIGGER_STOP:
0076         fifo_spdif_dcu_enable(component, false);
0077         break;
0078     default:
0079         return -EINVAL;
0080     }
0081 
0082     return 0;
0083 }
0084 
0085 static int fifo_spdif_prepare(struct snd_pcm_substream *substream,
0086                   struct snd_soc_dai *dai)
0087 {
0088     struct snd_soc_component *component = dai->component;
0089     int ret;
0090 
0091     ret = aiu_fifo_prepare(substream, dai);
0092     if (ret)
0093         return ret;
0094 
0095     snd_soc_component_update_bits(component,
0096                       AIU_MEM_IEC958_BUF_CNTL,
0097                       AIU_MEM_IEC958_BUF_CNTL_INIT,
0098                       AIU_MEM_IEC958_BUF_CNTL_INIT);
0099     snd_soc_component_update_bits(component,
0100                       AIU_MEM_IEC958_BUF_CNTL,
0101                       AIU_MEM_IEC958_BUF_CNTL_INIT, 0);
0102 
0103     return 0;
0104 }
0105 
0106 static int fifo_spdif_hw_params(struct snd_pcm_substream *substream,
0107                 struct snd_pcm_hw_params *params,
0108                 struct snd_soc_dai *dai)
0109 {
0110     struct snd_soc_component *component = dai->component;
0111     unsigned int val;
0112     int ret;
0113 
0114     ret = aiu_fifo_hw_params(substream, params, dai);
0115     if (ret)
0116         return ret;
0117 
0118     val = AIU_MEM_IEC958_CONTROL_RD_DDR |
0119           AIU_MEM_IEC958_CONTROL_MODE_LINEAR;
0120 
0121     switch (params_physical_width(params)) {
0122     case 16:
0123         val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;
0124         break;
0125     case 32:
0126         break;
0127     default:
0128         dev_err(dai->dev, "Unsupported physical width %u\n",
0129             params_physical_width(params));
0130         return -EINVAL;
0131     }
0132 
0133     snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL,
0134                       AIU_MEM_IEC958_CONTROL_ENDIAN |
0135                       AIU_MEM_IEC958_CONTROL_RD_DDR |
0136                       AIU_MEM_IEC958_CONTROL_MODE_LINEAR |
0137                       AIU_MEM_IEC958_CONTROL_MODE_16BIT,
0138                       val);
0139 
0140     /* Number bytes read by the FIFO between each IRQ */
0141     snd_soc_component_write(component, AIU_IEC958_BPF,
0142                 params_period_bytes(params));
0143 
0144     /*
0145      * AUTO_DISABLE and SYNC_HEAD are enabled by default but
0146      * this should be disabled in PCM (uncompressed) mode
0147      */
0148     snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
0149                       AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE |
0150                       AIU_IEC958_DCU_FF_CTRL_IRQ_MODE |
0151                       AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN,
0152                       AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ);
0153 
0154     return 0;
0155 }
0156 
0157 const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = {
0158     .trigger    = fifo_spdif_trigger,
0159     .prepare    = fifo_spdif_prepare,
0160     .hw_params  = fifo_spdif_hw_params,
0161     .startup    = aiu_fifo_startup,
0162     .shutdown   = aiu_fifo_shutdown,
0163 };
0164 
0165 int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai)
0166 {
0167     struct snd_soc_component *component = dai->component;
0168     struct aiu *aiu = snd_soc_component_get_drvdata(component);
0169     struct aiu_fifo *fifo;
0170     int ret;
0171 
0172     ret = aiu_fifo_dai_probe(dai);
0173     if (ret)
0174         return ret;
0175 
0176     fifo = dai->playback_dma_data;
0177 
0178     fifo->pcm = &fifo_spdif_pcm;
0179     fifo->mem_offset = AIU_MEM_IEC958_START;
0180     fifo->fifo_block = 1;
0181     fifo->pclk = aiu->spdif.clks[PCLK].clk;
0182     fifo->irq = aiu->spdif.irq;
0183 
0184     return 0;
0185 }