cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cygnus-pcm.c (22321B)


      1/*
      2 * Copyright (C) 2014-2015 Broadcom Corporation
      3 *
      4 * This program is free software; you can redistribute it and/or
      5 * modify it under the terms of the GNU General Public License as
      6 * published by the Free Software Foundation version 2.
      7 *
      8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
      9 * kind, whether express or implied; without even the implied warranty
     10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 * GNU General Public License for more details.
     12 */
     13#include <linux/debugfs.h>
     14#include <linux/dma-mapping.h>
     15#include <linux/init.h>
     16#include <linux/io.h>
     17#include <linux/module.h>
     18#include <linux/slab.h>
     19#include <linux/timer.h>
     20#include <sound/core.h>
     21#include <sound/pcm.h>
     22#include <sound/pcm_params.h>
     23#include <sound/soc.h>
     24#include <sound/soc-dai.h>
     25
     26#include "cygnus-ssp.h"
     27
     28/* Register offset needed for ASoC PCM module */
     29
     30#define INTH_R5F_STATUS_OFFSET     0x040
     31#define INTH_R5F_CLEAR_OFFSET      0x048
     32#define INTH_R5F_MASK_SET_OFFSET   0x050
     33#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
     34
     35#define BF_REARM_FREE_MARK_OFFSET 0x344
     36#define BF_REARM_FULL_MARK_OFFSET 0x348
     37
     38/* Ring Buffer Ctrl Regs --- Start */
     39/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
     40#define SRC_RBUF_0_RDADDR_OFFSET 0x500
     41#define SRC_RBUF_1_RDADDR_OFFSET 0x518
     42#define SRC_RBUF_2_RDADDR_OFFSET 0x530
     43#define SRC_RBUF_3_RDADDR_OFFSET 0x548
     44#define SRC_RBUF_4_RDADDR_OFFSET 0x560
     45#define SRC_RBUF_5_RDADDR_OFFSET 0x578
     46#define SRC_RBUF_6_RDADDR_OFFSET 0x590
     47
     48/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
     49#define SRC_RBUF_0_WRADDR_OFFSET 0x504
     50#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
     51#define SRC_RBUF_2_WRADDR_OFFSET 0x534
     52#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
     53#define SRC_RBUF_4_WRADDR_OFFSET 0x564
     54#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
     55#define SRC_RBUF_6_WRADDR_OFFSET 0x594
     56
     57/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
     58#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
     59#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
     60#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
     61#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
     62#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
     63#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
     64#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
     65
     66/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
     67#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
     68#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
     69#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
     70#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
     71#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
     72#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
     73#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
     74
     75/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
     76#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
     77#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
     78#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
     79#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
     80#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
     81#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
     82#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
     83
     84/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
     85#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
     86#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
     87#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
     88#define DST_RBUF_3_RDADDR_OFFSET 0x608
     89#define DST_RBUF_4_RDADDR_OFFSET 0x620
     90#define DST_RBUF_5_RDADDR_OFFSET 0x638
     91
     92/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
     93#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
     94#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
     95#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
     96#define DST_RBUF_3_WRADDR_OFFSET 0x60c
     97#define DST_RBUF_4_WRADDR_OFFSET 0x624
     98#define DST_RBUF_5_WRADDR_OFFSET 0x63c
     99
    100/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
    101#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
    102#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
    103#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
    104#define DST_RBUF_3_BASEADDR_OFFSET 0x610
    105#define DST_RBUF_4_BASEADDR_OFFSET 0x628
    106#define DST_RBUF_5_BASEADDR_OFFSET 0x640
    107
    108/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
    109#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
    110#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
    111#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
    112#define DST_RBUF_3_ENDADDR_OFFSET 0x614
    113#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
    114#define DST_RBUF_5_ENDADDR_OFFSET 0x644
    115
    116/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
    117#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
    118#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
    119#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
    120#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
    121#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
    122#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
    123/* Ring Buffer Ctrl Regs --- End */
    124
    125/* Error Status Regs --- Start */
    126/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
    127#define ESR0_STATUS_OFFSET 0x900
    128#define ESR1_STATUS_OFFSET 0x918
    129#define ESR2_STATUS_OFFSET 0x930
    130#define ESR3_STATUS_OFFSET 0x948
    131#define ESR4_STATUS_OFFSET 0x960
    132
    133/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
    134#define ESR0_STATUS_CLR_OFFSET 0x908
    135#define ESR1_STATUS_CLR_OFFSET 0x920
    136#define ESR2_STATUS_CLR_OFFSET 0x938
    137#define ESR3_STATUS_CLR_OFFSET 0x950
    138#define ESR4_STATUS_CLR_OFFSET 0x968
    139
    140/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
    141#define ESR0_MASK_STATUS_OFFSET 0x90c
    142#define ESR1_MASK_STATUS_OFFSET 0x924
    143#define ESR2_MASK_STATUS_OFFSET 0x93c
    144#define ESR3_MASK_STATUS_OFFSET 0x954
    145#define ESR4_MASK_STATUS_OFFSET 0x96c
    146
    147/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
    148#define ESR0_MASK_SET_OFFSET 0x910
    149#define ESR1_MASK_SET_OFFSET 0x928
    150#define ESR2_MASK_SET_OFFSET 0x940
    151#define ESR3_MASK_SET_OFFSET 0x958
    152#define ESR4_MASK_SET_OFFSET 0x970
    153
    154/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
    155#define ESR0_MASK_CLR_OFFSET 0x914
    156#define ESR1_MASK_CLR_OFFSET 0x92c
    157#define ESR2_MASK_CLR_OFFSET 0x944
    158#define ESR3_MASK_CLR_OFFSET 0x95c
    159#define ESR4_MASK_CLR_OFFSET 0x974
    160/* Error Status Regs --- End */
    161
    162#define R5F_ESR0_SHIFT  0    /* esr0 = fifo underflow */
    163#define R5F_ESR1_SHIFT  1    /* esr1 = ringbuf underflow */
    164#define R5F_ESR2_SHIFT  2    /* esr2 = ringbuf overflow */
    165#define R5F_ESR3_SHIFT  3    /* esr3 = freemark */
    166#define R5F_ESR4_SHIFT  4    /* esr4 = fullmark */
    167
    168
    169/* Mask for R5F register.  Set all relevant interrupt for playback handler */
    170#define ANY_PLAYBACK_IRQ  (BIT(R5F_ESR0_SHIFT) | \
    171			   BIT(R5F_ESR1_SHIFT) | \
    172			   BIT(R5F_ESR3_SHIFT))
    173
    174/* Mask for R5F register.  Set all relevant interrupt for capture handler */
    175#define ANY_CAPTURE_IRQ   (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
    176
    177/*
    178 * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
    179 * This number should be a multiple of 256. Minimum value is 256
    180 */
    181#define PERIOD_BYTES_MIN 0x100
    182
    183static const struct snd_pcm_hardware cygnus_pcm_hw = {
    184	.info = SNDRV_PCM_INFO_MMAP |
    185			SNDRV_PCM_INFO_MMAP_VALID |
    186			SNDRV_PCM_INFO_INTERLEAVED,
    187	.formats = SNDRV_PCM_FMTBIT_S16_LE |
    188			SNDRV_PCM_FMTBIT_S32_LE,
    189
    190	/* A period is basically an interrupt */
    191	.period_bytes_min = PERIOD_BYTES_MIN,
    192	.period_bytes_max = 0x10000,
    193
    194	/* period_min/max gives range of approx interrupts per buffer */
    195	.periods_min = 2,
    196	.periods_max = 8,
    197
    198	/*
    199	 * maximum buffer size in bytes = period_bytes_max * periods_max
    200	 * We allocate this amount of data for each enabled channel
    201	 */
    202	.buffer_bytes_max = 4 * 0x8000,
    203};
    204
    205static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
    206
    207static struct cygnus_aio_port *cygnus_dai_get_dma_data(
    208				struct snd_pcm_substream *substream)
    209{
    210	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
    211
    212	return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream);
    213}
    214
    215static void ringbuf_set_initial(void __iomem *audio_io,
    216		struct ringbuf_regs *p_rbuf,
    217		bool is_playback,
    218		u32 start,
    219		u32 periodsize,
    220		u32 bufsize)
    221{
    222	u32 initial_rd;
    223	u32 initial_wr;
    224	u32 end;
    225	u32 fmark_val; /* free or full mark */
    226
    227	p_rbuf->period_bytes = periodsize;
    228	p_rbuf->buf_size = bufsize;
    229
    230	if (is_playback) {
    231		/* Set the pointers to indicate full (flip uppermost bit) */
    232		initial_rd = start;
    233		initial_wr = initial_rd ^ BIT(31);
    234	} else {
    235		/* Set the pointers to indicate empty */
    236		initial_wr = start;
    237		initial_rd = initial_wr;
    238	}
    239
    240	end = start + bufsize - 1;
    241
    242	/*
    243	 * The interrupt will fire when free/full mark is *exceeded*
    244	 * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
    245	 * to be PERIOD_BYTES_MIN less than the period size.
    246	 */
    247	fmark_val = periodsize - PERIOD_BYTES_MIN;
    248
    249	writel(start, audio_io + p_rbuf->baseaddr);
    250	writel(end, audio_io + p_rbuf->endaddr);
    251	writel(fmark_val, audio_io + p_rbuf->fmark);
    252	writel(initial_rd, audio_io + p_rbuf->rdaddr);
    253	writel(initial_wr, audio_io + p_rbuf->wraddr);
    254}
    255
    256static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
    257{
    258	struct cygnus_aio_port *aio;
    259	struct ringbuf_regs *p_rbuf;
    260	int status = 0;
    261
    262	aio = cygnus_dai_get_dma_data(substream);
    263
    264	/* Map the ssp portnum to a set of ring buffers. */
    265	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    266		p_rbuf = &aio->play_rb_regs;
    267
    268		switch (aio->portnum) {
    269		case 0:
    270			*p_rbuf = RINGBUF_REG_PLAYBACK(0);
    271			break;
    272		case 1:
    273			*p_rbuf = RINGBUF_REG_PLAYBACK(2);
    274			break;
    275		case 2:
    276			*p_rbuf = RINGBUF_REG_PLAYBACK(4);
    277			break;
    278		case 3: /* SPDIF */
    279			*p_rbuf = RINGBUF_REG_PLAYBACK(6);
    280			break;
    281		default:
    282			status = -EINVAL;
    283		}
    284	} else {
    285		p_rbuf = &aio->capture_rb_regs;
    286
    287		switch (aio->portnum) {
    288		case 0:
    289			*p_rbuf = RINGBUF_REG_CAPTURE(0);
    290			break;
    291		case 1:
    292			*p_rbuf = RINGBUF_REG_CAPTURE(2);
    293			break;
    294		case 2:
    295			*p_rbuf = RINGBUF_REG_CAPTURE(4);
    296			break;
    297		default:
    298			status = -EINVAL;
    299		}
    300	}
    301
    302	return status;
    303}
    304
    305static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
    306{
    307	struct cygnus_aio_port *aio;
    308	struct ringbuf_regs *p_rbuf = NULL;
    309
    310	aio = cygnus_dai_get_dma_data(substream);
    311
    312	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    313		p_rbuf = &aio->play_rb_regs;
    314	else
    315		p_rbuf = &aio->capture_rb_regs;
    316
    317	return p_rbuf;
    318}
    319
    320static void enable_intr(struct snd_pcm_substream *substream)
    321{
    322	struct cygnus_aio_port *aio;
    323	u32 clear_mask;
    324
    325	aio = cygnus_dai_get_dma_data(substream);
    326
    327	/* The port number maps to the bit position to be cleared */
    328	clear_mask = BIT(aio->portnum);
    329
    330	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    331		/* Clear interrupt status before enabling them */
    332		writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
    333		writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
    334		writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
    335		/* Unmask the interrupts of the given port*/
    336		writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
    337		writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
    338		writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
    339
    340		writel(ANY_PLAYBACK_IRQ,
    341			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
    342	} else {
    343		writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
    344		writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
    345		writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
    346		writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
    347
    348		writel(ANY_CAPTURE_IRQ,
    349			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
    350	}
    351
    352}
    353
    354static void disable_intr(struct snd_pcm_substream *substream)
    355{
    356	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    357	struct cygnus_aio_port *aio;
    358	u32 set_mask;
    359
    360	aio = cygnus_dai_get_dma_data(substream);
    361
    362	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
    363
    364	/* The port number maps to the bit position to be set */
    365	set_mask = BIT(aio->portnum);
    366
    367	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    368		/* Mask the interrupts of the given port*/
    369		writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
    370		writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
    371		writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
    372	} else {
    373		writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
    374		writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
    375	}
    376
    377}
    378
    379static int cygnus_pcm_trigger(struct snd_soc_component *component,
    380			      struct snd_pcm_substream *substream, int cmd)
    381{
    382	int ret = 0;
    383
    384	switch (cmd) {
    385	case SNDRV_PCM_TRIGGER_START:
    386	case SNDRV_PCM_TRIGGER_RESUME:
    387		enable_intr(substream);
    388		break;
    389
    390	case SNDRV_PCM_TRIGGER_STOP:
    391	case SNDRV_PCM_TRIGGER_SUSPEND:
    392		disable_intr(substream);
    393		break;
    394	default:
    395		ret = -EINVAL;
    396	}
    397
    398	return ret;
    399}
    400
    401static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
    402{
    403	struct cygnus_aio_port *aio;
    404	struct ringbuf_regs *p_rbuf = NULL;
    405	u32 regval;
    406
    407	aio = cygnus_dai_get_dma_data(substream);
    408
    409	p_rbuf = get_ringbuf(substream);
    410
    411	/*
    412	 * If free/full mark interrupt occurs, provide timestamp
    413	 * to ALSA and update appropriate idx by period_bytes
    414	 */
    415	snd_pcm_period_elapsed(substream);
    416
    417	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    418		/* Set the ring buffer to full */
    419		regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
    420		regval = regval ^ BIT(31);
    421		writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
    422	} else {
    423		/* Set the ring buffer to empty */
    424		regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
    425		writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
    426	}
    427}
    428
    429/*
    430 * ESR0/1/3 status  Description
    431 *  0x1	I2S0_out port caused interrupt
    432 *  0x2	I2S1_out port caused interrupt
    433 *  0x4	I2S2_out port caused interrupt
    434 *  0x8	SPDIF_out port caused interrupt
    435 */
    436static void handle_playback_irq(struct cygnus_audio *cygaud)
    437{
    438	void __iomem *audio_io;
    439	u32 port;
    440	u32 esr_status0, esr_status1, esr_status3;
    441
    442	audio_io = cygaud->audio;
    443
    444	/*
    445	 * ESR status gets updates with/without interrupts enabled.
    446	 * So, check the ESR mask, which provides interrupt enable/
    447	 * disable status and use it to determine which ESR status
    448	 * should be serviced.
    449	 */
    450	esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
    451	esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
    452	esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
    453	esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
    454	esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
    455	esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
    456
    457	for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
    458		u32 esrmask = BIT(port);
    459
    460		/*
    461		 * Ringbuffer or FIFO underflow
    462		 * If we get this interrupt then, it is also true that we have
    463		 * not yet responded to the freemark interrupt.
    464		 * Log a debug message.  The freemark handler below will
    465		 * handle getting everything going again.
    466		 */
    467		if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
    468			dev_dbg(cygaud->dev,
    469				"Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
    470				esr_status0, esr_status1, esr_status3);
    471		}
    472
    473		/*
    474		 * Freemark is hit. This is the normal interrupt.
    475		 * In typical operation the read and write regs will be equal
    476		 */
    477		if (esrmask & esr_status3) {
    478			struct snd_pcm_substream *playstr;
    479
    480			playstr = cygaud->portinfo[port].play_stream;
    481			cygnus_pcm_period_elapsed(playstr);
    482		}
    483	}
    484
    485	/* Clear ESR interrupt */
    486	writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
    487	writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
    488	writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
    489	/* Rearm freemark logic by writing 1 to the correct bit */
    490	writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
    491}
    492
    493/*
    494 * ESR2/4 status  Description
    495 *  0x1	I2S0_in port caused interrupt
    496 *  0x2	I2S1_in port caused interrupt
    497 *  0x4	I2S2_in port caused interrupt
    498 */
    499static void handle_capture_irq(struct cygnus_audio *cygaud)
    500{
    501	void __iomem *audio_io;
    502	u32 port;
    503	u32 esr_status2, esr_status4;
    504
    505	audio_io = cygaud->audio;
    506
    507	/*
    508	 * ESR status gets updates with/without interrupts enabled.
    509	 * So, check the ESR mask, which provides interrupt enable/
    510	 * disable status and use it to determine which ESR status
    511	 * should be serviced.
    512	 */
    513	esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
    514	esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
    515	esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
    516	esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
    517
    518	for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
    519		u32 esrmask = BIT(port);
    520
    521		/*
    522		 * Ringbuffer or FIFO overflow
    523		 * If we get this interrupt then, it is also true that we have
    524		 * not yet responded to the fullmark interrupt.
    525		 * Log a debug message.  The fullmark handler below will
    526		 * handle getting everything going again.
    527		 */
    528		if (esrmask & esr_status2)
    529			dev_dbg(cygaud->dev,
    530				"Overflow: esr2=0x%x\n", esr_status2);
    531
    532		if (esrmask & esr_status4) {
    533			struct snd_pcm_substream *capstr;
    534
    535			capstr = cygaud->portinfo[port].capture_stream;
    536			cygnus_pcm_period_elapsed(capstr);
    537		}
    538	}
    539
    540	writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
    541	writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
    542	/* Rearm fullmark logic by writing 1 to the correct bit */
    543	writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
    544}
    545
    546static irqreturn_t cygnus_dma_irq(int irq, void *data)
    547{
    548	u32 r5_status;
    549	struct cygnus_audio *cygaud = data;
    550
    551	/*
    552	 * R5 status bits	Description
    553	 *  0		ESR0 (playback FIFO interrupt)
    554	 *  1		ESR1 (playback rbuf interrupt)
    555	 *  2		ESR2 (capture rbuf interrupt)
    556	 *  3		ESR3 (Freemark play. interrupt)
    557	 *  4		ESR4 (Fullmark capt. interrupt)
    558	 */
    559	r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
    560
    561	if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
    562		return IRQ_NONE;
    563
    564	/* If playback interrupt happened */
    565	if (ANY_PLAYBACK_IRQ & r5_status) {
    566		handle_playback_irq(cygaud);
    567		writel(ANY_PLAYBACK_IRQ & r5_status,
    568			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
    569	}
    570
    571	/* If  capture interrupt happened */
    572	if (ANY_CAPTURE_IRQ & r5_status) {
    573		handle_capture_irq(cygaud);
    574		writel(ANY_CAPTURE_IRQ & r5_status,
    575			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
    576	}
    577
    578	return IRQ_HANDLED;
    579}
    580
    581static int cygnus_pcm_open(struct snd_soc_component *component,
    582			   struct snd_pcm_substream *substream)
    583{
    584	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    585	struct snd_pcm_runtime *runtime = substream->runtime;
    586	struct cygnus_aio_port *aio;
    587	int ret;
    588
    589	aio = cygnus_dai_get_dma_data(substream);
    590	if (!aio)
    591		return -ENODEV;
    592
    593	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
    594
    595	snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
    596
    597	ret = snd_pcm_hw_constraint_step(runtime, 0,
    598		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
    599	if (ret < 0)
    600		return ret;
    601
    602	ret = snd_pcm_hw_constraint_step(runtime, 0,
    603		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
    604	if (ret < 0)
    605		return ret;
    606	/*
    607	 * Keep track of which substream belongs to which port.
    608	 * This info is needed by snd_pcm_period_elapsed() in irq_handler
    609	 */
    610	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    611		aio->play_stream = substream;
    612	else
    613		aio->capture_stream = substream;
    614
    615	return 0;
    616}
    617
    618static int cygnus_pcm_close(struct snd_soc_component *component,
    619			    struct snd_pcm_substream *substream)
    620{
    621	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    622	struct cygnus_aio_port *aio;
    623
    624	aio = cygnus_dai_get_dma_data(substream);
    625
    626	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s  port %d\n", __func__, aio->portnum);
    627
    628	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    629		aio->play_stream = NULL;
    630	else
    631		aio->capture_stream = NULL;
    632
    633	if (!aio->play_stream && !aio->capture_stream)
    634		dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed  port %d\n", aio->portnum);
    635
    636	return 0;
    637}
    638
    639static int cygnus_pcm_prepare(struct snd_soc_component *component,
    640			      struct snd_pcm_substream *substream)
    641{
    642	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    643	struct snd_pcm_runtime *runtime = substream->runtime;
    644	struct cygnus_aio_port *aio;
    645	unsigned long bufsize, periodsize;
    646	bool is_play;
    647	u32 start;
    648	struct ringbuf_regs *p_rbuf = NULL;
    649
    650	aio = cygnus_dai_get_dma_data(substream);
    651	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
    652
    653	bufsize = snd_pcm_lib_buffer_bytes(substream);
    654	periodsize = snd_pcm_lib_period_bytes(substream);
    655
    656	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
    657			__func__, bufsize, periodsize);
    658
    659	configure_ringbuf_regs(substream);
    660
    661	p_rbuf = get_ringbuf(substream);
    662
    663	start = runtime->dma_addr;
    664
    665	is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
    666
    667	ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
    668				periodsize, bufsize);
    669
    670	return 0;
    671}
    672
    673static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
    674					    struct snd_pcm_substream *substream)
    675{
    676	struct cygnus_aio_port *aio;
    677	unsigned int res = 0, cur = 0, base = 0;
    678	struct ringbuf_regs *p_rbuf = NULL;
    679
    680	aio = cygnus_dai_get_dma_data(substream);
    681
    682	/*
    683	 * Get the offset of the current read (for playack) or write
    684	 * index (for capture).  Report this value back to the asoc framework.
    685	 */
    686	p_rbuf = get_ringbuf(substream);
    687	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    688		cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
    689	else
    690		cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
    691
    692	base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
    693
    694	/*
    695	 * Mask off the MSB of the rdaddr,wraddr and baseaddr
    696	 * since MSB is not part of the address
    697	 */
    698	res = (cur & 0x7fffffff) - (base & 0x7fffffff);
    699
    700	return bytes_to_frames(substream->runtime, res);
    701}
    702
    703static int cygnus_dma_new(struct snd_soc_component *component,
    704			  struct snd_soc_pcm_runtime *rtd)
    705{
    706	size_t size = cygnus_pcm_hw.buffer_bytes_max;
    707	struct snd_card *card = rtd->card->snd_card;
    708
    709	if (!card->dev->dma_mask)
    710		card->dev->dma_mask = &cygnus_dma_dmamask;
    711	if (!card->dev->coherent_dma_mask)
    712		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
    713
    714	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
    715				       card->dev, size, size);
    716
    717	return 0;
    718}
    719
    720static struct snd_soc_component_driver cygnus_soc_platform = {
    721	.open		= cygnus_pcm_open,
    722	.close		= cygnus_pcm_close,
    723	.prepare	= cygnus_pcm_prepare,
    724	.trigger	= cygnus_pcm_trigger,
    725	.pointer	= cygnus_pcm_pointer,
    726	.pcm_construct	= cygnus_dma_new,
    727};
    728
    729int cygnus_soc_platform_register(struct device *dev,
    730				 struct cygnus_audio *cygaud)
    731{
    732	int rc;
    733
    734	dev_dbg(dev, "%s Enter\n", __func__);
    735
    736	rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
    737				IRQF_SHARED, "cygnus-audio", cygaud);
    738	if (rc) {
    739		dev_err(dev, "%s request_irq error %d\n", __func__, rc);
    740		return rc;
    741	}
    742
    743	rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
    744					     NULL, 0);
    745	if (rc) {
    746		dev_err(dev, "%s failed\n", __func__);
    747		return rc;
    748	}
    749
    750	return 0;
    751}
    752
    753int cygnus_soc_platform_unregister(struct device *dev)
    754{
    755	return 0;
    756}
    757
    758MODULE_LICENSE("GPL v2");
    759MODULE_AUTHOR("Broadcom");
    760MODULE_DESCRIPTION("Cygnus ASoC PCM module");