File Index
Files and directories
Sign in
FILE

cs5535audio_pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/pci/cs5535audio/cs5535audio_pcm.c C
  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, 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	for (i = 0; i < periods; i++) {
135		struct cs5535audio_dma_desc *desc =
136			&((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
137		desc->addr = cpu_to_le32(addr);
138		desc->size = cpu_to_le16(period_bytes);
139		desc->ctlreserved = cpu_to_le16(PRD_EOP);
140		addr += period_bytes;
141	}
142	/* we reserved one dummy descriptor at the end to do the PRD jump */
143	lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
144	lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
145	lastdesc->size = 0;
146	lastdesc->ctlreserved = cpu_to_le16(PRD_JMP);
147	jmpprd_addr = (u32)dma->desc_buf.addr +
148		sizeof(struct cs5535audio_dma_desc) * periods;
149
150	dma->substream = substream;
151	dma->period_bytes = period_bytes;
152	dma->periods = periods;
153	guard(spinlock_irq)(&cs5535au->reg_lock);
154	dma->ops->disable_dma(cs5535au);
155	dma->ops->setup_prd(cs5535au, jmpprd_addr);
156	return 0;
157}
158
159static void cs5535audio_playback_enable_dma(struct cs5535audio *cs5535au)
160{
161	cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_EN);
162}
163
164static void cs5535audio_playback_disable_dma(struct cs5535audio *cs5535au)
165{
166	cs_writeb(cs5535au, ACC_BM0_CMD, 0);
167}
168
169static void cs5535audio_playback_pause_dma(struct cs5535audio *cs5535au)
170{
171	cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_PAUSE);
172}
173
174static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au,
175					   u32 prd_addr)
176{
177	cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
178}
179
180static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
181{
182	return cs_readl(cs5535au, ACC_BM0_PRD);
183}
184
185static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
186{
187	return cs_readl(cs5535au, ACC_BM0_PNTR);
188}
189
190static void cs5535audio_capture_enable_dma(struct cs5535audio *cs5535au)
191{
192	cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_EN);
193}
194
195static void cs5535audio_capture_disable_dma(struct cs5535audio *cs5535au)
196{
197	cs_writeb(cs5535au, ACC_BM1_CMD, 0);
198}
199
200static void cs5535audio_capture_pause_dma(struct cs5535audio *cs5535au)
201{
202	cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_PAUSE);
203}
204
205static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au,
206					  u32 prd_addr)
207{
208	cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
209}
210
211static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
212{
213	return cs_readl(cs5535au, ACC_BM1_PRD);
214}
215
216static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
217{
218	return cs_readl(cs5535au, ACC_BM1_PNTR);
219}
220
221static void cs5535audio_clear_dma_packets(struct cs5535audio *cs5535au,
222					  struct cs5535audio_dma *dma,
223					  struct snd_pcm_substream *substream)
224{
225	snd_dma_free_pages(&dma->desc_buf);
226	dma->desc_buf.area = NULL;
227	dma->substream = NULL;
228}
229
230static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
231				     struct snd_pcm_hw_params *hw_params)
232{
233	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
234	struct cs5535audio_dma *dma = substream->runtime->private_data;
235	int err;
236
237	dma->buf_addr = substream->runtime->dma_addr;
238	dma->buf_bytes = params_buffer_bytes(hw_params);
239
240	err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
241					    params_periods(hw_params),
242					    params_period_bytes(hw_params));
243	if (!err)
244		dma->pcm_open_flag = 1;
245
246	return err;
247}
248
249static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
250{
251	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
252	struct cs5535audio_dma *dma = substream->runtime->private_data;
253
254	if (dma->pcm_open_flag) {
255		if (substream == cs5535au->playback_substream)
256			snd_ac97_update_power(cs5535au->ac97,
257					AC97_PCM_FRONT_DAC_RATE, 0);
258		else
259			snd_ac97_update_power(cs5535au->ac97,
260					AC97_PCM_LR_ADC_RATE, 0);
261		dma->pcm_open_flag = 0;
262	}
263	cs5535audio_clear_dma_packets(cs5535au, dma, substream);
264	return 0;
265}
266
267static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
268{
269	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
270	return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_FRONT_DAC_RATE,
271				 substream->runtime->rate);
272}
273
274static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
275{
276	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
277	struct cs5535audio_dma *dma = substream->runtime->private_data;
278
279	guard(spinlock)(&cs5535au->reg_lock);
280	switch (cmd) {
281	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
282		dma->ops->pause_dma(cs5535au);
283		break;
284	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
285		dma->ops->enable_dma(cs5535au);
286		break;
287	case SNDRV_PCM_TRIGGER_START:
288		dma->ops->enable_dma(cs5535au);
289		break;
290	case SNDRV_PCM_TRIGGER_RESUME:
291		dma->ops->enable_dma(cs5535au);
292		break;
293	case SNDRV_PCM_TRIGGER_STOP:
294		dma->ops->disable_dma(cs5535au);
295		break;
296	case SNDRV_PCM_TRIGGER_SUSPEND:
297		dma->ops->disable_dma(cs5535au);
298		break;
299	default:
300		dev_err(cs5535au->card->dev, "unhandled trigger\n");
301		return -EINVAL;
302	}
303	return 0;
304}
305
306static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
307							*substream)
308{
309	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
310	u32 curdma;
311	struct cs5535audio_dma *dma;
312
313	dma = substream->runtime->private_data;
314	curdma = dma->ops->read_dma_pntr(cs5535au);
315	if (curdma < dma->buf_addr) {
316		dev_err(cs5535au->card->dev, "curdma=%x < %x bufaddr.\n",
317					curdma, dma->buf_addr);
318		return 0;
319	}
320	curdma -= dma->buf_addr;
321	if (curdma >= dma->buf_bytes) {
322		dev_err(cs5535au->card->dev, "diff=%x >= %x buf_bytes.\n",
323					curdma, dma->buf_bytes);
324		return 0;
325	}
326	return bytes_to_frames(substream->runtime, curdma);
327}
328
329static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
330{
331	int err;
332	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
333	struct snd_pcm_runtime *runtime = substream->runtime;
334
335	runtime->hw = snd_cs5535audio_capture;
336	runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
337	snd_pcm_limit_hw_rates(runtime);
338	cs5535au->capture_substream = substream;
339	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
340	err = snd_pcm_hw_constraint_integer(runtime,
341					    SNDRV_PCM_HW_PARAM_PERIODS);
342	if (err < 0)
343		return err;
344	olpc_capture_open(cs5535au->ac97);
345	return 0;
346}
347
348static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
349{
350	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
351	olpc_capture_close(cs5535au->ac97);
352	return 0;
353}
354
355static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
356{
357	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
358	return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_LR_ADC_RATE,
359				 substream->runtime->rate);
360}
361
362static const struct snd_pcm_ops snd_cs5535audio_playback_ops = {
363	.open =		snd_cs5535audio_playback_open,
364	.close =	snd_cs5535audio_playback_close,
365	.hw_params =	snd_cs5535audio_hw_params,
366	.hw_free =	snd_cs5535audio_hw_free,
367	.prepare =	snd_cs5535audio_playback_prepare,
368	.trigger =	snd_cs5535audio_trigger,
369	.pointer =	snd_cs5535audio_pcm_pointer,
370};
371
372static const struct snd_pcm_ops snd_cs5535audio_capture_ops = {
373	.open =		snd_cs5535audio_capture_open,
374	.close =	snd_cs5535audio_capture_close,
375	.hw_params =	snd_cs5535audio_hw_params,
376	.hw_free =	snd_cs5535audio_hw_free,
377	.prepare =	snd_cs5535audio_capture_prepare,
378	.trigger =	snd_cs5535audio_trigger,
379	.pointer =	snd_cs5535audio_pcm_pointer,
380};
381
382static const struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
383        .type = CS5535AUDIO_DMA_PLAYBACK,
384        .enable_dma = cs5535audio_playback_enable_dma,
385        .disable_dma = cs5535audio_playback_disable_dma,
386        .setup_prd = cs5535audio_playback_setup_prd,
387        .read_prd = cs5535audio_playback_read_prd,
388        .pause_dma = cs5535audio_playback_pause_dma,
389        .read_dma_pntr = cs5535audio_playback_read_dma_pntr,
390};
391
392static const struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
393        .type = CS5535AUDIO_DMA_CAPTURE,
394        .enable_dma = cs5535audio_capture_enable_dma,
395        .disable_dma = cs5535audio_capture_disable_dma,
396        .setup_prd = cs5535audio_capture_setup_prd,
397        .read_prd = cs5535audio_capture_read_prd,
398        .pause_dma = cs5535audio_capture_pause_dma,
399        .read_dma_pntr = cs5535audio_capture_read_dma_pntr,
400};
401
402int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
403{
404	struct snd_pcm *pcm;
405	int err;
406
407	err = snd_pcm_new(cs5535au->card, "CS5535 Audio", 0, 1, 1, &pcm);
408	if (err < 0)
409		return err;
410
411	cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK].ops =
412					&snd_cs5535audio_playback_dma_ops;
413	cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE].ops =
414					&snd_cs5535audio_capture_dma_ops;
415	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
416					&snd_cs5535audio_playback_ops);
417	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
418					&snd_cs5535audio_capture_ops);
419
420	pcm->private_data = cs5535au;
421	pcm->info_flags = 0;
422	strscpy(pcm->name, "CS5535 Audio");
423
424	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
425				       &cs5535au->pci->dev,
426				       64*1024, 128*1024);
427	cs5535au->pcm = pcm;
428
429	return 0;
430}
431
~/ Read-only file preview.