~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/amd/acp/acp-i2s.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2 //
  3 // This file is provided under a dual BSD/GPLv2 license. When using or
  4 // redistributing this file, you may do so under either license.
  5 //
  6 // Copyright(c) 2021 Advanced Micro Devices, Inc.
  7 //
  8 // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
  9 //
 10 
 11 /*
 12  * Generic Hardware interface for ACP Audio I2S controller
 13  */
 14 
 15 #include <linux/platform_device.h>
 16 #include <linux/module.h>
 17 #include <linux/err.h>
 18 #include <linux/io.h>
 19 #include <sound/pcm_params.h>
 20 #include <sound/soc.h>
 21 #include <sound/soc-dai.h>
 22 #include <linux/dma-mapping.h>
 23 #include <linux/bitfield.h>
 24 
 25 #include "amd.h"
 26 
 27 #define DRV_NAME "acp_i2s_playcap"
 28 #define I2S_MASTER_MODE_ENABLE          1
 29 #define LRCLK_DIV_FIELD                 GENMASK(10, 2)
 30 #define BCLK_DIV_FIELD                  GENMASK(23, 11)
 31 #define ACP63_LRCLK_DIV_FIELD           GENMASK(12, 2)
 32 #define ACP63_BCLK_DIV_FIELD            GENMASK(23, 13)
 33 
 34 static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id)
 35 {
 36         u32 i2s_clk_reg, val;
 37         struct acp_chip_info *chip;
 38         struct device *dev;
 39 
 40         dev = adata->dev;
 41         chip = dev_get_platdata(dev);
 42         switch (dai_id) {
 43         case I2S_SP_INSTANCE:
 44                 i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
 45                 break;
 46         case I2S_BT_INSTANCE:
 47                 i2s_clk_reg = ACP_I2STDM1_MSTRCLKGEN;
 48                 break;
 49         case I2S_HS_INSTANCE:
 50                 i2s_clk_reg = ACP_I2STDM2_MSTRCLKGEN;
 51                 break;
 52         default:
 53                 i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
 54                 break;
 55         }
 56 
 57         val  = I2S_MASTER_MODE_ENABLE;
 58         if (adata->tdm_mode)
 59                 val |= BIT(1);
 60 
 61         switch (chip->acp_rev) {
 62         case ACP63_DEV:
 63                 val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, adata->lrclk_div);
 64                 val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, adata->bclk_div);
 65                 break;
 66         default:
 67                 val |= FIELD_PREP(LRCLK_DIV_FIELD, adata->lrclk_div);
 68                 val |= FIELD_PREP(BCLK_DIV_FIELD, adata->bclk_div);
 69         }
 70         writel(val, adata->acp_base + i2s_clk_reg);
 71 }
 72 
 73 static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 74                            unsigned int fmt)
 75 {
 76         struct acp_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai);
 77         int mode;
 78 
 79         mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 80         switch (mode) {
 81         case SND_SOC_DAIFMT_I2S:
 82                 adata->tdm_mode = TDM_DISABLE;
 83                 break;
 84         case SND_SOC_DAIFMT_DSP_A:
 85                 adata->tdm_mode = TDM_ENABLE;
 86                 break;
 87         default:
 88                 return -EINVAL;
 89         }
 90         return 0;
 91 }
 92 
 93 static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask,
 94                                 int slots, int slot_width)
 95 {
 96         struct device *dev = dai->component->dev;
 97         struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai);
 98         struct acp_stream *stream;
 99         int slot_len, no_of_slots;
100 
101         switch (slot_width) {
102         case SLOT_WIDTH_8:
103                 slot_len = 8;
104                 break;
105         case SLOT_WIDTH_16:
106                 slot_len = 16;
107                 break;
108         case SLOT_WIDTH_24:
109                 slot_len = 24;
110                 break;
111         case SLOT_WIDTH_32:
112                 slot_len = 0;
113                 break;
114         default:
115                 dev_err(dev, "Unsupported bitdepth %d\n", slot_width);
116                 return -EINVAL;
117         }
118 
119         switch (slots) {
120         case 1 ... 7:
121                 no_of_slots = slots;
122                 break;
123         case 8:
124                 no_of_slots = 0;
125                 break;
126         default:
127                 dev_err(dev, "Unsupported slots %d\n", slots);
128                 return -EINVAL;
129         }
130 
131         slots = no_of_slots;
132 
133         spin_lock_irq(&adata->acp_lock);
134         list_for_each_entry(stream, &adata->stream_list, list) {
135                 if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
136                         adata->tdm_tx_fmt[stream->dai_id - 1] =
137                                         FRM_LEN | (slots << 15) | (slot_len << 18);
138                 else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
139                         adata->tdm_rx_fmt[stream->dai_id - 1] =
140                                         FRM_LEN | (slots << 15) | (slot_len << 18);
141         }
142         spin_unlock_irq(&adata->acp_lock);
143         return 0;
144 }
145 
146 static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
147                             struct snd_soc_dai *dai)
148 {
149         struct device *dev = dai->component->dev;
150         struct acp_dev_data *adata;
151         struct acp_resource *rsrc;
152         u32 val;
153         u32 xfer_resolution;
154         u32 reg_val, fmt_reg, tdm_fmt;
155         u32 lrclk_div_val, bclk_div_val;
156 
157         adata = snd_soc_dai_get_drvdata(dai);
158         rsrc = adata->rsrc;
159 
160         /* These values are as per Hardware Spec */
161         switch (params_format(params)) {
162         case SNDRV_PCM_FORMAT_U8:
163         case SNDRV_PCM_FORMAT_S8:
164                 xfer_resolution = 0x0;
165                 break;
166         case SNDRV_PCM_FORMAT_S16_LE:
167                 xfer_resolution = 0x02;
168                 break;
169         case SNDRV_PCM_FORMAT_S24_LE:
170                 xfer_resolution = 0x04;
171                 break;
172         case SNDRV_PCM_FORMAT_S32_LE:
173                 xfer_resolution = 0x05;
174                 break;
175         default:
176                 return -EINVAL;
177         }
178 
179         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
180                 switch (dai->driver->id) {
181                 case I2S_BT_INSTANCE:
182                         reg_val = ACP_BTTDM_ITER;
183                         fmt_reg = ACP_BTTDM_TXFRMT;
184                         break;
185                 case I2S_SP_INSTANCE:
186                         reg_val = ACP_I2STDM_ITER;
187                         fmt_reg = ACP_I2STDM_TXFRMT;
188                         break;
189                 case I2S_HS_INSTANCE:
190                         reg_val = ACP_HSTDM_ITER;
191                         fmt_reg = ACP_HSTDM_TXFRMT;
192                         break;
193                 default:
194                         dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
195                         return -EINVAL;
196                 }
197                 adata->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution;
198         } else {
199                 switch (dai->driver->id) {
200                 case I2S_BT_INSTANCE:
201                         reg_val = ACP_BTTDM_IRER;
202                         fmt_reg = ACP_BTTDM_RXFRMT;
203                         break;
204                 case I2S_SP_INSTANCE:
205                         reg_val = ACP_I2STDM_IRER;
206                         fmt_reg = ACP_I2STDM_RXFRMT;
207                         break;
208                 case I2S_HS_INSTANCE:
209                         reg_val = ACP_HSTDM_IRER;
210                         fmt_reg = ACP_HSTDM_RXFRMT;
211                         break;
212                 default:
213                         dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
214                         return -EINVAL;
215                 }
216                 adata->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution;
217         }
218 
219         val = readl(adata->acp_base + reg_val);
220         val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
221         val = val | (xfer_resolution  << 3);
222         writel(val, adata->acp_base + reg_val);
223 
224         if (adata->tdm_mode) {
225                 val = readl(adata->acp_base + reg_val);
226                 writel(val | BIT(1), adata->acp_base + reg_val);
227                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
228                         tdm_fmt = adata->tdm_tx_fmt[dai->driver->id - 1];
229                 else
230                         tdm_fmt = adata->tdm_rx_fmt[dai->driver->id - 1];
231                 writel(tdm_fmt, adata->acp_base + fmt_reg);
232         }
233 
234         if (rsrc->soc_mclk) {
235                 switch (params_format(params)) {
236                 case SNDRV_PCM_FORMAT_S16_LE:
237                         switch (params_rate(params)) {
238                         case 8000:
239                                 bclk_div_val = 768;
240                                 break;
241                         case 16000:
242                                 bclk_div_val = 384;
243                                 break;
244                         case 24000:
245                                 bclk_div_val = 256;
246                                 break;
247                         case 32000:
248                                 bclk_div_val = 192;
249                                 break;
250                         case 44100:
251                         case 48000:
252                                 bclk_div_val = 128;
253                                 break;
254                         case 88200:
255                         case 96000:
256                                 bclk_div_val = 64;
257                                 break;
258                         case 192000:
259                                 bclk_div_val = 32;
260                                 break;
261                         default:
262                                 return -EINVAL;
263                         }
264                         lrclk_div_val = 32;
265                         break;
266                 case SNDRV_PCM_FORMAT_S32_LE:
267                         switch (params_rate(params)) {
268                         case 8000:
269                                 bclk_div_val = 384;
270                                 break;
271                         case 16000:
272                                 bclk_div_val = 192;
273                                 break;
274                         case 24000:
275                                 bclk_div_val = 128;
276                                 break;
277                         case 32000:
278                                 bclk_div_val = 96;
279                                 break;
280                         case 44100:
281                         case 48000:
282                                 bclk_div_val = 64;
283                                 break;
284                         case 88200:
285                         case 96000:
286                                 bclk_div_val = 32;
287                                 break;
288                         case 192000:
289                                 bclk_div_val = 16;
290                                 break;
291                         default:
292                                 return -EINVAL;
293                         }
294                         lrclk_div_val = 64;
295                         break;
296                 default:
297                         return -EINVAL;
298                 }
299                 adata->lrclk_div = lrclk_div_val;
300                 adata->bclk_div = bclk_div_val;
301         }
302         return 0;
303 }
304 
305 static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
306 {
307         struct acp_stream *stream = substream->runtime->private_data;
308         struct device *dev = dai->component->dev;
309         struct acp_dev_data *adata = dev_get_drvdata(dev);
310         struct acp_resource *rsrc = adata->rsrc;
311         u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
312 
313         period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
314         buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
315 
316         switch (cmd) {
317         case SNDRV_PCM_TRIGGER_START:
318         case SNDRV_PCM_TRIGGER_RESUME:
319         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
320                 stream->bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
321                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
322                         switch (dai->driver->id) {
323                         case I2S_BT_INSTANCE:
324                                 water_val = ACP_BT_TX_INTR_WATERMARK_SIZE;
325                                 reg_val = ACP_BTTDM_ITER;
326                                 ier_val = ACP_BTTDM_IER;
327                                 buf_reg = ACP_BT_TX_RINGBUFSIZE;
328                                 break;
329                         case I2S_SP_INSTANCE:
330                                 water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE;
331                                 reg_val = ACP_I2STDM_ITER;
332                                 ier_val = ACP_I2STDM_IER;
333                                 buf_reg = ACP_I2S_TX_RINGBUFSIZE;
334                                 break;
335                         case I2S_HS_INSTANCE:
336                                 water_val = ACP_HS_TX_INTR_WATERMARK_SIZE;
337                                 reg_val = ACP_HSTDM_ITER;
338                                 ier_val = ACP_HSTDM_IER;
339                                 buf_reg = ACP_HS_TX_RINGBUFSIZE;
340                                 break;
341                         default:
342                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
343                                 return -EINVAL;
344                         }
345                 } else {
346                         switch (dai->driver->id) {
347                         case I2S_BT_INSTANCE:
348                                 water_val = ACP_BT_RX_INTR_WATERMARK_SIZE;
349                                 reg_val = ACP_BTTDM_IRER;
350                                 ier_val = ACP_BTTDM_IER;
351                                 buf_reg = ACP_BT_RX_RINGBUFSIZE;
352                                 break;
353                         case I2S_SP_INSTANCE:
354                                 water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE;
355                                 reg_val = ACP_I2STDM_IRER;
356                                 ier_val = ACP_I2STDM_IER;
357                                 buf_reg = ACP_I2S_RX_RINGBUFSIZE;
358                                 break;
359                         case I2S_HS_INSTANCE:
360                                 water_val = ACP_HS_RX_INTR_WATERMARK_SIZE;
361                                 reg_val = ACP_HSTDM_IRER;
362                                 ier_val = ACP_HSTDM_IER;
363                                 buf_reg = ACP_HS_RX_RINGBUFSIZE;
364                                 break;
365                         default:
366                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
367                                 return -EINVAL;
368                         }
369                 }
370                 writel(period_bytes, adata->acp_base + water_val);
371                 writel(buf_size, adata->acp_base + buf_reg);
372                 if (rsrc->soc_mclk)
373                         acp_set_i2s_clk(adata, dai->driver->id);
374                 val = readl(adata->acp_base + reg_val);
375                 val = val | BIT(0);
376                 writel(val, adata->acp_base + reg_val);
377                 writel(1, adata->acp_base + ier_val);
378                 return 0;
379         case SNDRV_PCM_TRIGGER_STOP:
380         case SNDRV_PCM_TRIGGER_SUSPEND:
381         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
382                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
383                         switch (dai->driver->id) {
384                         case I2S_BT_INSTANCE:
385                                 reg_val = ACP_BTTDM_ITER;
386                                 break;
387                         case I2S_SP_INSTANCE:
388                                 reg_val = ACP_I2STDM_ITER;
389                                 break;
390                         case I2S_HS_INSTANCE:
391                                 reg_val = ACP_HSTDM_ITER;
392                                 break;
393                         default:
394                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
395                                 return -EINVAL;
396                         }
397 
398                 } else {
399                         switch (dai->driver->id) {
400                         case I2S_BT_INSTANCE:
401                                 reg_val = ACP_BTTDM_IRER;
402                                 break;
403                         case I2S_SP_INSTANCE:
404                                 reg_val = ACP_I2STDM_IRER;
405                                 break;
406                         case I2S_HS_INSTANCE:
407                                 reg_val = ACP_HSTDM_IRER;
408                                 break;
409                         default:
410                                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
411                                 return -EINVAL;
412                         }
413                 }
414                 val = readl(adata->acp_base + reg_val);
415                 val = val & ~BIT(0);
416                 writel(val, adata->acp_base + reg_val);
417 
418                 if (!(readl(adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) &&
419                     !(readl(adata->acp_base + ACP_BTTDM_IRER) & BIT(0)))
420                         writel(0, adata->acp_base + ACP_BTTDM_IER);
421                 if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
422                     !(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
423                         writel(0, adata->acp_base + ACP_I2STDM_IER);
424                 if (!(readl(adata->acp_base + ACP_HSTDM_ITER) & BIT(0)) &&
425                     !(readl(adata->acp_base + ACP_HSTDM_IRER) & BIT(0)))
426                         writel(0, adata->acp_base + ACP_HSTDM_IER);
427                 return 0;
428         default:
429                 return -EINVAL;
430         }
431 
432         return 0;
433 }
434 
435 static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
436 {
437         struct device *dev = dai->component->dev;
438         struct acp_dev_data *adata = dev_get_drvdata(dev);
439         struct acp_resource *rsrc = adata->rsrc;
440         struct acp_stream *stream = substream->runtime->private_data;
441         u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
442         u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
443         unsigned int dir = substream->stream;
444 
445         switch (dai->driver->id) {
446         case I2S_SP_INSTANCE:
447                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
448                         reg_dma_size = ACP_I2S_TX_DMA_SIZE;
449                         acp_fifo_addr = rsrc->sram_pte_offset +
450                                                 SP_PB_FIFO_ADDR_OFFSET;
451                         reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
452                         reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
453 
454                         phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
455                         writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
456                 } else {
457                         reg_dma_size = ACP_I2S_RX_DMA_SIZE;
458                         acp_fifo_addr = rsrc->sram_pte_offset +
459                                                 SP_CAPT_FIFO_ADDR_OFFSET;
460                         reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
461                         reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
462                         phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
463                         writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR);
464                 }
465                 break;
466         case I2S_BT_INSTANCE:
467                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
468                         reg_dma_size = ACP_BT_TX_DMA_SIZE;
469                         acp_fifo_addr = rsrc->sram_pte_offset +
470                                                 BT_PB_FIFO_ADDR_OFFSET;
471                         reg_fifo_addr = ACP_BT_TX_FIFOADDR;
472                         reg_fifo_size = ACP_BT_TX_FIFOSIZE;
473 
474                         phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
475                         writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
476                 } else {
477                         reg_dma_size = ACP_BT_RX_DMA_SIZE;
478                         acp_fifo_addr = rsrc->sram_pte_offset +
479                                                 BT_CAPT_FIFO_ADDR_OFFSET;
480                         reg_fifo_addr = ACP_BT_RX_FIFOADDR;
481                         reg_fifo_size = ACP_BT_RX_FIFOSIZE;
482 
483                         phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
484                         writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
485                 }
486                 break;
487         case I2S_HS_INSTANCE:
488                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
489                         reg_dma_size = ACP_HS_TX_DMA_SIZE;
490                         acp_fifo_addr = rsrc->sram_pte_offset +
491                                 HS_PB_FIFO_ADDR_OFFSET;
492                         reg_fifo_addr = ACP_HS_TX_FIFOADDR;
493                         reg_fifo_size = ACP_HS_TX_FIFOSIZE;
494 
495                         phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
496                         writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR);
497                 } else {
498                         reg_dma_size = ACP_HS_RX_DMA_SIZE;
499                         acp_fifo_addr = rsrc->sram_pte_offset +
500                                         HS_CAPT_FIFO_ADDR_OFFSET;
501                         reg_fifo_addr = ACP_HS_RX_FIFOADDR;
502                         reg_fifo_size = ACP_HS_RX_FIFOSIZE;
503 
504                         phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
505                         writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR);
506                 }
507                 break;
508         default:
509                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
510                 return -EINVAL;
511         }
512 
513         writel(DMA_SIZE, adata->acp_base + reg_dma_size);
514         writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
515         writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
516 
517         ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
518         ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
519                         BIT(BT_RX_THRESHOLD(rsrc->offset)) |
520                         BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
521                         BIT(BT_TX_THRESHOLD(rsrc->offset)) |
522                         BIT(HS_RX_THRESHOLD(rsrc->offset)) |
523                         BIT(HS_TX_THRESHOLD(rsrc->offset));
524 
525         writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
526 
527         return 0;
528 }
529 
530 static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
531 {
532         struct acp_stream *stream = substream->runtime->private_data;
533         struct device *dev = dai->component->dev;
534         struct acp_dev_data *adata = dev_get_drvdata(dev);
535         struct acp_resource *rsrc = adata->rsrc;
536         unsigned int dir = substream->stream;
537         unsigned int irq_bit = 0;
538 
539         switch (dai->driver->id) {
540         case I2S_SP_INSTANCE:
541                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
542                         irq_bit = BIT(I2S_TX_THRESHOLD(rsrc->offset));
543                         stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
544                         stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
545                 } else {
546                         irq_bit = BIT(I2S_RX_THRESHOLD(rsrc->offset));
547                         stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
548                         stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
549                 }
550                 break;
551         case I2S_BT_INSTANCE:
552                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
553                         irq_bit = BIT(BT_TX_THRESHOLD(rsrc->offset));
554                         stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
555                         stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
556                 } else {
557                         irq_bit = BIT(BT_RX_THRESHOLD(rsrc->offset));
558                         stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
559                         stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
560                 }
561                 break;
562         case I2S_HS_INSTANCE:
563                 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
564                         irq_bit = BIT(HS_TX_THRESHOLD(rsrc->offset));
565                         stream->pte_offset = ACP_SRAM_HS_PB_PTE_OFFSET;
566                         stream->fifo_offset = HS_PB_FIFO_ADDR_OFFSET;
567                 } else {
568                         irq_bit = BIT(HS_RX_THRESHOLD(rsrc->offset));
569                         stream->pte_offset = ACP_SRAM_HS_CP_PTE_OFFSET;
570                         stream->fifo_offset = HS_CAPT_FIFO_ADDR_OFFSET;
571                 }
572                 break;
573         default:
574                 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
575                 return -EINVAL;
576         }
577 
578         /* Save runtime dai configuration in stream */
579         stream->id = dai->driver->id + dir;
580         stream->dai_id = dai->driver->id;
581         stream->irq_bit = irq_bit;
582         stream->dir = substream->stream;
583 
584         return 0;
585 }
586 
587 const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
588         .startup        = acp_i2s_startup,
589         .hw_params      = acp_i2s_hwparams,
590         .prepare        = acp_i2s_prepare,
591         .trigger        = acp_i2s_trigger,
592         .set_fmt        = acp_i2s_set_fmt,
593         .set_tdm_slot   = acp_i2s_set_tdm_slot,
594 };
595 EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);
596 
597 MODULE_DESCRIPTION("AMD ACP Audio I2S controller");
598 MODULE_LICENSE("Dual BSD/GPL");
599 MODULE_ALIAS(DRV_NAME);
600 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php