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

cs5535audio_pcm.c (12564B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Driver for audio on multifunction CS5535 companion device
      4 * Copyright (C) Jaya Kumar
      5 *
      6 * Based on Jaroslav Kysela and Takashi Iwai's examples.
      7 * This work was sponsored by CIS(M) Sdn Bhd.
      8 *
      9 * todo: add be fmt support, spdif, pm
     10 */
     11
     12#include <linux/init.h>
     13#include <linux/pci.h>
     14#include <sound/core.h>
     15#include <sound/control.h>
     16#include <sound/initval.h>
     17#include <sound/asoundef.h>
     18#include <sound/pcm.h>
     19#include <sound/pcm_params.h>
     20#include <sound/ac97_codec.h>
     21#include "cs5535audio.h"
     22
     23static const struct snd_pcm_hardware snd_cs5535audio_playback =
     24{
     25	.info =			(
     26				SNDRV_PCM_INFO_MMAP |
     27				SNDRV_PCM_INFO_INTERLEAVED |
     28		 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
     29		 		SNDRV_PCM_INFO_MMAP_VALID |
     30		 		SNDRV_PCM_INFO_PAUSE |
     31				SNDRV_PCM_INFO_RESUME
     32				),
     33	.formats =		(
     34				SNDRV_PCM_FMTBIT_S16_LE
     35				),
     36	.rates =		(
     37				SNDRV_PCM_RATE_CONTINUOUS |
     38				SNDRV_PCM_RATE_8000_48000
     39				),
     40	.rate_min =		4000,
     41	.rate_max =		48000,
     42	.channels_min =		2,
     43	.channels_max =		2,
     44	.buffer_bytes_max =	(128*1024),
     45	.period_bytes_min =	64,
     46	.period_bytes_max =	(64*1024 - 16),
     47	.periods_min =		1,
     48	.periods_max =		CS5535AUDIO_MAX_DESCRIPTORS,
     49	.fifo_size =		0,
     50};
     51
     52static const struct snd_pcm_hardware snd_cs5535audio_capture =
     53{
     54	.info =			(
     55				SNDRV_PCM_INFO_MMAP |
     56				SNDRV_PCM_INFO_INTERLEAVED |
     57		 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
     58		 		SNDRV_PCM_INFO_MMAP_VALID
     59				),
     60	.formats =		(
     61				SNDRV_PCM_FMTBIT_S16_LE
     62				),
     63	.rates =		(
     64				SNDRV_PCM_RATE_CONTINUOUS |
     65				SNDRV_PCM_RATE_8000_48000
     66				),
     67	.rate_min =		4000,
     68	.rate_max =		48000,
     69	.channels_min =		2,
     70	.channels_max =		2,
     71	.buffer_bytes_max =	(128*1024),
     72	.period_bytes_min =	64,
     73	.period_bytes_max =	(64*1024 - 16),
     74	.periods_min =		1,
     75	.periods_max =		CS5535AUDIO_MAX_DESCRIPTORS,
     76	.fifo_size =		0,
     77};
     78
     79static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
     80{
     81	int err;
     82	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
     83	struct snd_pcm_runtime *runtime = substream->runtime;
     84
     85	runtime->hw = snd_cs5535audio_playback;
     86	runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC];
     87	snd_pcm_limit_hw_rates(runtime);
     88	cs5535au->playback_substream = substream;
     89	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
     90	err = snd_pcm_hw_constraint_integer(runtime,
     91					    SNDRV_PCM_HW_PARAM_PERIODS);
     92	if (err < 0)
     93		return err;
     94
     95	return 0;
     96}
     97
     98static int snd_cs5535audio_playback_close(struct snd_pcm_substream *substream)
     99{
    100	return 0;
    101}
    102
    103#define CS5535AUDIO_DESC_LIST_SIZE \
    104	PAGE_ALIGN(CS5535AUDIO_MAX_DESCRIPTORS * sizeof(struct cs5535audio_dma_desc))
    105
    106static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
    107					 struct cs5535audio_dma *dma,
    108					 struct snd_pcm_substream *substream,
    109					 unsigned int periods,
    110					 unsigned int period_bytes)
    111{
    112	unsigned int i;
    113	u32 addr, desc_addr, jmpprd_addr;
    114	struct cs5535audio_dma_desc *lastdesc;
    115
    116	if (periods > CS5535AUDIO_MAX_DESCRIPTORS)
    117		return -ENOMEM;
    118
    119	if (dma->desc_buf.area == NULL) {
    120		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
    121					&cs5535au->pci->dev,
    122					CS5535AUDIO_DESC_LIST_SIZE+1,
    123					&dma->desc_buf) < 0)
    124			return -ENOMEM;
    125		dma->period_bytes = dma->periods = 0;
    126	}
    127
    128	if (dma->periods == periods && dma->period_bytes == period_bytes)
    129		return 0;
    130
    131	/* the u32 cast is okay because in snd*create we successfully told
    132	   pci alloc that we're only 32 bit capable so the upper will be 0 */
    133	addr = (u32) substream->runtime->dma_addr;
    134	desc_addr = (u32) dma->desc_buf.addr;
    135	for (i = 0; i < periods; i++) {
    136		struct cs5535audio_dma_desc *desc =
    137			&((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
    138		desc->addr = cpu_to_le32(addr);
    139		desc->size = cpu_to_le16(period_bytes);
    140		desc->ctlreserved = cpu_to_le16(PRD_EOP);
    141		desc_addr += sizeof(struct cs5535audio_dma_desc);
    142		addr += period_bytes;
    143	}
    144	/* we reserved one dummy descriptor at the end to do the PRD jump */
    145	lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
    146	lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
    147	lastdesc->size = 0;
    148	lastdesc->ctlreserved = cpu_to_le16(PRD_JMP);
    149	jmpprd_addr = (u32)dma->desc_buf.addr +
    150		sizeof(struct cs5535audio_dma_desc) * periods;
    151
    152	dma->substream = substream;
    153	dma->period_bytes = period_bytes;
    154	dma->periods = periods;
    155	spin_lock_irq(&cs5535au->reg_lock);
    156	dma->ops->disable_dma(cs5535au);
    157	dma->ops->setup_prd(cs5535au, jmpprd_addr);
    158	spin_unlock_irq(&cs5535au->reg_lock);
    159	return 0;
    160}
    161
    162static void cs5535audio_playback_enable_dma(struct cs5535audio *cs5535au)
    163{
    164	cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_EN);
    165}
    166
    167static void cs5535audio_playback_disable_dma(struct cs5535audio *cs5535au)
    168{
    169	cs_writeb(cs5535au, ACC_BM0_CMD, 0);
    170}
    171
    172static void cs5535audio_playback_pause_dma(struct cs5535audio *cs5535au)
    173{
    174	cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_PAUSE);
    175}
    176
    177static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au,
    178					   u32 prd_addr)
    179{
    180	cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
    181}
    182
    183static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
    184{
    185	return cs_readl(cs5535au, ACC_BM0_PRD);
    186}
    187
    188static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
    189{
    190	return cs_readl(cs5535au, ACC_BM0_PNTR);
    191}
    192
    193static void cs5535audio_capture_enable_dma(struct cs5535audio *cs5535au)
    194{
    195	cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_EN);
    196}
    197
    198static void cs5535audio_capture_disable_dma(struct cs5535audio *cs5535au)
    199{
    200	cs_writeb(cs5535au, ACC_BM1_CMD, 0);
    201}
    202
    203static void cs5535audio_capture_pause_dma(struct cs5535audio *cs5535au)
    204{
    205	cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_PAUSE);
    206}
    207
    208static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au,
    209					  u32 prd_addr)
    210{
    211	cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
    212}
    213
    214static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
    215{
    216	return cs_readl(cs5535au, ACC_BM1_PRD);
    217}
    218
    219static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
    220{
    221	return cs_readl(cs5535au, ACC_BM1_PNTR);
    222}
    223
    224static void cs5535audio_clear_dma_packets(struct cs5535audio *cs5535au,
    225					  struct cs5535audio_dma *dma,
    226					  struct snd_pcm_substream *substream)
    227{
    228	snd_dma_free_pages(&dma->desc_buf);
    229	dma->desc_buf.area = NULL;
    230	dma->substream = NULL;
    231}
    232
    233static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
    234				     struct snd_pcm_hw_params *hw_params)
    235{
    236	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    237	struct cs5535audio_dma *dma = substream->runtime->private_data;
    238	int err;
    239
    240	dma->buf_addr = substream->runtime->dma_addr;
    241	dma->buf_bytes = params_buffer_bytes(hw_params);
    242
    243	err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
    244					    params_periods(hw_params),
    245					    params_period_bytes(hw_params));
    246	if (!err)
    247		dma->pcm_open_flag = 1;
    248
    249	return err;
    250}
    251
    252static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
    253{
    254	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    255	struct cs5535audio_dma *dma = substream->runtime->private_data;
    256
    257	if (dma->pcm_open_flag) {
    258		if (substream == cs5535au->playback_substream)
    259			snd_ac97_update_power(cs5535au->ac97,
    260					AC97_PCM_FRONT_DAC_RATE, 0);
    261		else
    262			snd_ac97_update_power(cs5535au->ac97,
    263					AC97_PCM_LR_ADC_RATE, 0);
    264		dma->pcm_open_flag = 0;
    265	}
    266	cs5535audio_clear_dma_packets(cs5535au, dma, substream);
    267	return 0;
    268}
    269
    270static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
    271{
    272	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    273	return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_FRONT_DAC_RATE,
    274				 substream->runtime->rate);
    275}
    276
    277static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
    278{
    279	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    280	struct cs5535audio_dma *dma = substream->runtime->private_data;
    281	int err = 0;
    282
    283	spin_lock(&cs5535au->reg_lock);
    284	switch (cmd) {
    285	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    286		dma->ops->pause_dma(cs5535au);
    287		break;
    288	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    289		dma->ops->enable_dma(cs5535au);
    290		break;
    291	case SNDRV_PCM_TRIGGER_START:
    292		dma->ops->enable_dma(cs5535au);
    293		break;
    294	case SNDRV_PCM_TRIGGER_RESUME:
    295		dma->ops->enable_dma(cs5535au);
    296		break;
    297	case SNDRV_PCM_TRIGGER_STOP:
    298		dma->ops->disable_dma(cs5535au);
    299		break;
    300	case SNDRV_PCM_TRIGGER_SUSPEND:
    301		dma->ops->disable_dma(cs5535au);
    302		break;
    303	default:
    304		dev_err(cs5535au->card->dev, "unhandled trigger\n");
    305		err = -EINVAL;
    306		break;
    307	}
    308	spin_unlock(&cs5535au->reg_lock);
    309	return err;
    310}
    311
    312static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
    313							*substream)
    314{
    315	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    316	u32 curdma;
    317	struct cs5535audio_dma *dma;
    318
    319	dma = substream->runtime->private_data;
    320	curdma = dma->ops->read_dma_pntr(cs5535au);
    321	if (curdma < dma->buf_addr) {
    322		dev_err(cs5535au->card->dev, "curdma=%x < %x bufaddr.\n",
    323					curdma, dma->buf_addr);
    324		return 0;
    325	}
    326	curdma -= dma->buf_addr;
    327	if (curdma >= dma->buf_bytes) {
    328		dev_err(cs5535au->card->dev, "diff=%x >= %x buf_bytes.\n",
    329					curdma, dma->buf_bytes);
    330		return 0;
    331	}
    332	return bytes_to_frames(substream->runtime, curdma);
    333}
    334
    335static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
    336{
    337	int err;
    338	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    339	struct snd_pcm_runtime *runtime = substream->runtime;
    340
    341	runtime->hw = snd_cs5535audio_capture;
    342	runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
    343	snd_pcm_limit_hw_rates(runtime);
    344	cs5535au->capture_substream = substream;
    345	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
    346	err = snd_pcm_hw_constraint_integer(runtime,
    347					    SNDRV_PCM_HW_PARAM_PERIODS);
    348	if (err < 0)
    349		return err;
    350	olpc_capture_open(cs5535au->ac97);
    351	return 0;
    352}
    353
    354static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
    355{
    356	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    357	olpc_capture_close(cs5535au->ac97);
    358	return 0;
    359}
    360
    361static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
    362{
    363	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
    364	return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_LR_ADC_RATE,
    365				 substream->runtime->rate);
    366}
    367
    368static const struct snd_pcm_ops snd_cs5535audio_playback_ops = {
    369	.open =		snd_cs5535audio_playback_open,
    370	.close =	snd_cs5535audio_playback_close,
    371	.hw_params =	snd_cs5535audio_hw_params,
    372	.hw_free =	snd_cs5535audio_hw_free,
    373	.prepare =	snd_cs5535audio_playback_prepare,
    374	.trigger =	snd_cs5535audio_trigger,
    375	.pointer =	snd_cs5535audio_pcm_pointer,
    376};
    377
    378static const struct snd_pcm_ops snd_cs5535audio_capture_ops = {
    379	.open =		snd_cs5535audio_capture_open,
    380	.close =	snd_cs5535audio_capture_close,
    381	.hw_params =	snd_cs5535audio_hw_params,
    382	.hw_free =	snd_cs5535audio_hw_free,
    383	.prepare =	snd_cs5535audio_capture_prepare,
    384	.trigger =	snd_cs5535audio_trigger,
    385	.pointer =	snd_cs5535audio_pcm_pointer,
    386};
    387
    388static const struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
    389        .type = CS5535AUDIO_DMA_PLAYBACK,
    390        .enable_dma = cs5535audio_playback_enable_dma,
    391        .disable_dma = cs5535audio_playback_disable_dma,
    392        .setup_prd = cs5535audio_playback_setup_prd,
    393        .read_prd = cs5535audio_playback_read_prd,
    394        .pause_dma = cs5535audio_playback_pause_dma,
    395        .read_dma_pntr = cs5535audio_playback_read_dma_pntr,
    396};
    397
    398static const struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
    399        .type = CS5535AUDIO_DMA_CAPTURE,
    400        .enable_dma = cs5535audio_capture_enable_dma,
    401        .disable_dma = cs5535audio_capture_disable_dma,
    402        .setup_prd = cs5535audio_capture_setup_prd,
    403        .read_prd = cs5535audio_capture_read_prd,
    404        .pause_dma = cs5535audio_capture_pause_dma,
    405        .read_dma_pntr = cs5535audio_capture_read_dma_pntr,
    406};
    407
    408int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
    409{
    410	struct snd_pcm *pcm;
    411	int err;
    412
    413	err = snd_pcm_new(cs5535au->card, "CS5535 Audio", 0, 1, 1, &pcm);
    414	if (err < 0)
    415		return err;
    416
    417	cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK].ops =
    418					&snd_cs5535audio_playback_dma_ops;
    419	cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE].ops =
    420					&snd_cs5535audio_capture_dma_ops;
    421	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
    422					&snd_cs5535audio_playback_ops);
    423	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
    424					&snd_cs5535audio_capture_ops);
    425
    426	pcm->private_data = cs5535au;
    427	pcm->info_flags = 0;
    428	strcpy(pcm->name, "CS5535 Audio");
    429
    430	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
    431				       &cs5535au->pci->dev,
    432				       64*1024, 128*1024);
    433	cs5535au->pcm = pcm;
    434
    435	return 0;
    436}
    437