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

TOMOYO Linux Cross Reference
Linux/sound/sh/sh_dac_audio.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ 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-or-later
  2 /*
  3  * sh_dac_audio.c - SuperH DAC audio driver for ALSA
  4  *
  5  * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
  6  *
  7  * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
  8  */
  9 
 10 #include <linux/hrtimer.h>
 11 #include <linux/interrupt.h>
 12 #include <linux/io.h>
 13 #include <linux/platform_device.h>
 14 #include <linux/slab.h>
 15 #include <linux/module.h>
 16 #include <sound/core.h>
 17 #include <sound/initval.h>
 18 #include <sound/pcm.h>
 19 #include <sound/sh_dac_audio.h>
 20 #include <asm/clock.h>
 21 #include <asm/hd64461.h>
 22 #include <mach/hp6xx.h>
 23 #include <cpu/dac.h>
 24 
 25 MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
 26 MODULE_DESCRIPTION("SuperH DAC audio driver");
 27 MODULE_LICENSE("GPL");
 28 
 29 /* Module Parameters */
 30 static int index = SNDRV_DEFAULT_IDX1;
 31 static char *id = SNDRV_DEFAULT_STR1;
 32 module_param(index, int, 0444);
 33 MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
 34 module_param(id, charp, 0444);
 35 MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
 36 
 37 /* main struct */
 38 struct snd_sh_dac {
 39         struct snd_card *card;
 40         struct snd_pcm_substream *substream;
 41         struct hrtimer hrtimer;
 42         ktime_t wakeups_per_second;
 43 
 44         int rate;
 45         int empty;
 46         char *data_buffer, *buffer_begin, *buffer_end;
 47         int processed; /* bytes proccesed, to compare with period_size */
 48         int buffer_size;
 49         struct dac_audio_pdata *pdata;
 50 };
 51 
 52 
 53 static void dac_audio_start_timer(struct snd_sh_dac *chip)
 54 {
 55         hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
 56                       HRTIMER_MODE_REL);
 57 }
 58 
 59 static void dac_audio_stop_timer(struct snd_sh_dac *chip)
 60 {
 61         hrtimer_cancel(&chip->hrtimer);
 62 }
 63 
 64 static void dac_audio_reset(struct snd_sh_dac *chip)
 65 {
 66         dac_audio_stop_timer(chip);
 67         chip->buffer_begin = chip->buffer_end = chip->data_buffer;
 68         chip->processed = 0;
 69         chip->empty = 1;
 70 }
 71 
 72 static void dac_audio_set_rate(struct snd_sh_dac *chip)
 73 {
 74         chip->wakeups_per_second = 1000000000 / chip->rate;
 75 }
 76 
 77 
 78 /* PCM INTERFACE */
 79 
 80 static const struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
 81         .info                   = (SNDRV_PCM_INFO_MMAP |
 82                                         SNDRV_PCM_INFO_MMAP_VALID |
 83                                         SNDRV_PCM_INFO_INTERLEAVED |
 84                                         SNDRV_PCM_INFO_HALF_DUPLEX),
 85         .formats                = SNDRV_PCM_FMTBIT_U8,
 86         .rates                  = SNDRV_PCM_RATE_8000,
 87         .rate_min               = 8000,
 88         .rate_max               = 8000,
 89         .channels_min           = 1,
 90         .channels_max           = 1,
 91         .buffer_bytes_max       = (48*1024),
 92         .period_bytes_min       = 1,
 93         .period_bytes_max       = (48*1024),
 94         .periods_min            = 1,
 95         .periods_max            = 1024,
 96 };
 97 
 98 static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
 99 {
100         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
101         struct snd_pcm_runtime *runtime = substream->runtime;
102 
103         runtime->hw = snd_sh_dac_pcm_hw;
104 
105         chip->substream = substream;
106         chip->buffer_begin = chip->buffer_end = chip->data_buffer;
107         chip->processed = 0;
108         chip->empty = 1;
109 
110         chip->pdata->start(chip->pdata);
111 
112         return 0;
113 }
114 
115 static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
116 {
117         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
118 
119         chip->substream = NULL;
120 
121         dac_audio_stop_timer(chip);
122         chip->pdata->stop(chip->pdata);
123 
124         return 0;
125 }
126 
127 static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
128 {
129         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
130         struct snd_pcm_runtime *runtime = chip->substream->runtime;
131 
132         chip->buffer_size = runtime->buffer_size;
133         memset(chip->data_buffer, 0, chip->pdata->buffer_size);
134 
135         return 0;
136 }
137 
138 static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
139 {
140         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
141 
142         switch (cmd) {
143         case SNDRV_PCM_TRIGGER_START:
144                 dac_audio_start_timer(chip);
145                 break;
146         case SNDRV_PCM_TRIGGER_STOP:
147                 chip->buffer_begin = chip->buffer_end = chip->data_buffer;
148                 chip->processed = 0;
149                 chip->empty = 1;
150                 dac_audio_stop_timer(chip);
151                 break;
152         default:
153                  return -EINVAL;
154         }
155 
156         return 0;
157 }
158 
159 static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
160                                int channel, unsigned long pos,
161                                struct iov_iter *src, unsigned long count)
162 {
163         /* channel is not used (interleaved data) */
164         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
165 
166         if (copy_from_iter_toio(chip->data_buffer + pos, src, count))
167                 return -EFAULT;
168         chip->buffer_end = chip->data_buffer + pos + count;
169 
170         if (chip->empty) {
171                 chip->empty = 0;
172                 dac_audio_start_timer(chip);
173         }
174 
175         return 0;
176 }
177 
178 static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
179                                   int channel, unsigned long pos,
180                                   unsigned long count)
181 {
182         /* channel is not used (interleaved data) */
183         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
184 
185         memset_io(chip->data_buffer + pos, 0, count);
186         chip->buffer_end = chip->data_buffer + pos + count;
187 
188         if (chip->empty) {
189                 chip->empty = 0;
190                 dac_audio_start_timer(chip);
191         }
192 
193         return 0;
194 }
195 
196 static
197 snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
198 {
199         struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
200         int pointer = chip->buffer_begin - chip->data_buffer;
201 
202         return pointer;
203 }
204 
205 /* pcm ops */
206 static const struct snd_pcm_ops snd_sh_dac_pcm_ops = {
207         .open           = snd_sh_dac_pcm_open,
208         .close          = snd_sh_dac_pcm_close,
209         .prepare        = snd_sh_dac_pcm_prepare,
210         .trigger        = snd_sh_dac_pcm_trigger,
211         .pointer        = snd_sh_dac_pcm_pointer,
212         .copy           = snd_sh_dac_pcm_copy,
213         .fill_silence   = snd_sh_dac_pcm_silence,
214         .mmap           = snd_pcm_lib_mmap_iomem,
215 };
216 
217 static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
218 {
219         int err;
220         struct snd_pcm *pcm;
221 
222         /* device should be always 0 for us */
223         err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
224         if (err < 0)
225                 return err;
226 
227         pcm->private_data = chip;
228         strcpy(pcm->name, "SH_DAC PCM");
229         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
230 
231         /* buffer size=48K */
232         snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
233                                        NULL, 48 * 1024, 48 * 1024);
234 
235         return 0;
236 }
237 /* END OF PCM INTERFACE */
238 
239 
240 /* driver .remove  --  destructor */
241 static void snd_sh_dac_remove(struct platform_device *devptr)
242 {
243         snd_card_free(platform_get_drvdata(devptr));
244 }
245 
246 /* free -- it has been defined by create */
247 static int snd_sh_dac_free(struct snd_sh_dac *chip)
248 {
249         /* release the data */
250         kfree(chip->data_buffer);
251         kfree(chip);
252 
253         return 0;
254 }
255 
256 static int snd_sh_dac_dev_free(struct snd_device *device)
257 {
258         struct snd_sh_dac *chip = device->device_data;
259 
260         return snd_sh_dac_free(chip);
261 }
262 
263 static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
264 {
265         struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
266                                                hrtimer);
267         struct snd_pcm_runtime *runtime = chip->substream->runtime;
268         ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
269 
270         if (!chip->empty) {
271                 sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
272                 chip->buffer_begin++;
273 
274                 chip->processed++;
275                 if (chip->processed >= b_ps) {
276                         chip->processed -= b_ps;
277                         snd_pcm_period_elapsed(chip->substream);
278                 }
279 
280                 if (chip->buffer_begin == (chip->data_buffer +
281                                            chip->buffer_size - 1))
282                         chip->buffer_begin = chip->data_buffer;
283 
284                 if (chip->buffer_begin == chip->buffer_end)
285                         chip->empty = 1;
286 
287         }
288 
289         if (!chip->empty)
290                 hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
291                               HRTIMER_MODE_REL);
292 
293         return HRTIMER_NORESTART;
294 }
295 
296 /* create  --  chip-specific constructor for the cards components */
297 static int snd_sh_dac_create(struct snd_card *card,
298                              struct platform_device *devptr,
299                              struct snd_sh_dac **rchip)
300 {
301         struct snd_sh_dac *chip;
302         int err;
303 
304         static const struct snd_device_ops ops = {
305                    .dev_free = snd_sh_dac_dev_free,
306         };
307 
308         *rchip = NULL;
309 
310         chip = kzalloc(sizeof(*chip), GFP_KERNEL);
311         if (chip == NULL)
312                 return -ENOMEM;
313 
314         chip->card = card;
315 
316         hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
317         chip->hrtimer.function = sh_dac_audio_timer;
318 
319         dac_audio_reset(chip);
320         chip->rate = 8000;
321         dac_audio_set_rate(chip);
322 
323         chip->pdata = devptr->dev.platform_data;
324 
325         chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
326         if (chip->data_buffer == NULL) {
327                 kfree(chip);
328                 return -ENOMEM;
329         }
330 
331         err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
332         if (err < 0) {
333                 snd_sh_dac_free(chip);
334                 return err;
335         }
336 
337         *rchip = chip;
338 
339         return 0;
340 }
341 
342 /* driver .probe  --  constructor */
343 static int snd_sh_dac_probe(struct platform_device *devptr)
344 {
345         struct snd_sh_dac *chip;
346         struct snd_card *card;
347         int err;
348 
349         err = snd_card_new(&devptr->dev, index, id, THIS_MODULE, 0, &card);
350         if (err < 0) {
351                         snd_printk(KERN_ERR "cannot allocate the card\n");
352                         return err;
353         }
354 
355         err = snd_sh_dac_create(card, devptr, &chip);
356         if (err < 0)
357                 goto probe_error;
358 
359         err = snd_sh_dac_pcm(chip, 0);
360         if (err < 0)
361                 goto probe_error;
362 
363         strcpy(card->driver, "snd_sh_dac");
364         strcpy(card->shortname, "SuperH DAC audio driver");
365         printk(KERN_INFO "%s %s", card->longname, card->shortname);
366 
367         err = snd_card_register(card);
368         if (err < 0)
369                 goto probe_error;
370 
371         snd_printk(KERN_INFO "ALSA driver for SuperH DAC audio");
372 
373         platform_set_drvdata(devptr, card);
374         return 0;
375 
376 probe_error:
377         snd_card_free(card);
378         return err;
379 }
380 
381 /*
382  * "driver" definition
383  */
384 static struct platform_driver sh_dac_driver = {
385         .probe  = snd_sh_dac_probe,
386         .remove_new = snd_sh_dac_remove,
387         .driver = {
388                 .name = "dac_audio",
389         },
390 };
391 
392 module_platform_driver(sh_dac_driver);
393 

~ [ 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