Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* Copyright 2011 Broadcom Corporation. All rights reserved. */ 3 : : 4 : : #include <linux/interrupt.h> 5 : : #include <linux/slab.h> 6 : : 7 : : #include <sound/asoundef.h> 8 : : 9 : : #include "bcm2835.h" 10 : : 11 : : /* hardware definition */ 12 : : static const struct snd_pcm_hardware snd_bcm2835_playback_hw = { 13 : : .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 14 : : SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 15 : : SNDRV_PCM_INFO_SYNC_APPLPTR), 16 : : .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 17 : : .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, 18 : : .rate_min = 8000, 19 : : .rate_max = 192000, 20 : : .channels_min = 1, 21 : : .channels_max = 2, 22 : : .buffer_bytes_max = 128 * 1024, 23 : : .period_bytes_min = 1 * 1024, 24 : : .period_bytes_max = 128 * 1024, 25 : : .periods_min = 1, 26 : : .periods_max = 128, 27 : : }; 28 : : 29 : : static const struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { 30 : : .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 31 : : SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 32 : : SNDRV_PCM_INFO_SYNC_APPLPTR), 33 : : .formats = SNDRV_PCM_FMTBIT_S16_LE, 34 : : .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 35 : : SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | 36 : : SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, 37 : : .rate_min = 44100, 38 : : .rate_max = 192000, 39 : : .channels_min = 2, 40 : : .channels_max = 8, 41 : : .buffer_bytes_max = 512 * 1024, 42 : : .period_bytes_min = 1 * 1024, 43 : : .period_bytes_max = 512 * 1024, 44 : : .periods_min = 1, 45 : : .periods_max = 128, 46 : : }; 47 : : 48 : 0 : static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) 49 : : { 50 : 0 : kfree(runtime->private_data); 51 : 0 : } 52 : : 53 : 0 : void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream, 54 : : unsigned int bytes) 55 : : { 56 : 0 : struct snd_pcm_substream *substream = alsa_stream->substream; 57 : : unsigned int pos; 58 : : 59 : 0 : if (!alsa_stream->period_size) 60 : : return; 61 : : 62 : 0 : if (bytes >= alsa_stream->buffer_size) { 63 : 0 : snd_pcm_stream_lock(substream); 64 : 0 : snd_pcm_stop(substream, 65 : 0 : alsa_stream->draining ? 66 : : SNDRV_PCM_STATE_SETUP : 67 : : SNDRV_PCM_STATE_XRUN); 68 : 0 : snd_pcm_stream_unlock(substream); 69 : 0 : return; 70 : : } 71 : : 72 : : pos = atomic_read(&alsa_stream->pos); 73 : 0 : pos += bytes; 74 : 0 : pos %= alsa_stream->buffer_size; 75 : : atomic_set(&alsa_stream->pos, pos); 76 : : 77 : 0 : alsa_stream->period_offset += bytes; 78 : 0 : alsa_stream->interpolate_start = ktime_get(); 79 : 0 : if (alsa_stream->period_offset >= alsa_stream->period_size) { 80 : 0 : alsa_stream->period_offset %= alsa_stream->period_size; 81 : 0 : snd_pcm_period_elapsed(substream); 82 : : } 83 : : } 84 : : 85 : : /* open callback */ 86 : 0 : static int snd_bcm2835_playback_open_generic( 87 : : struct snd_pcm_substream *substream, int spdif) 88 : : { 89 : 0 : struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); 90 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 91 : : struct bcm2835_alsa_stream *alsa_stream; 92 : : int idx; 93 : : int err; 94 : : 95 : 0 : mutex_lock(&chip->audio_mutex); 96 : 0 : idx = substream->number; 97 : : 98 : 0 : if (spdif && chip->opened) { 99 : : err = -EBUSY; 100 : : goto out; 101 : 0 : } else if (!spdif && (chip->opened & (1 << idx))) { 102 : : err = -EBUSY; 103 : : goto out; 104 : : } 105 : 0 : if (idx >= MAX_SUBSTREAMS) { 106 : 0 : dev_err(chip->dev, 107 : : "substream(%d) device doesn't exist max(%d) substreams allowed\n", 108 : : idx, MAX_SUBSTREAMS); 109 : : err = -ENODEV; 110 : 0 : goto out; 111 : : } 112 : : 113 : 0 : alsa_stream = kzalloc(sizeof(*alsa_stream), GFP_KERNEL); 114 : 0 : if (!alsa_stream) { 115 : : err = -ENOMEM; 116 : : goto out; 117 : : } 118 : : 119 : : /* Initialise alsa_stream */ 120 : 0 : alsa_stream->chip = chip; 121 : 0 : alsa_stream->substream = substream; 122 : 0 : alsa_stream->idx = idx; 123 : : 124 : 0 : err = bcm2835_audio_open(alsa_stream); 125 : 0 : if (err) { 126 : 0 : kfree(alsa_stream); 127 : 0 : goto out; 128 : : } 129 : 0 : runtime->private_data = alsa_stream; 130 : 0 : runtime->private_free = snd_bcm2835_playback_free; 131 : 0 : if (spdif) { 132 : 0 : runtime->hw = snd_bcm2835_playback_spdif_hw; 133 : : } else { 134 : : /* clear spdif status, as we are not in spdif mode */ 135 : 0 : chip->spdif_status = 0; 136 : 0 : runtime->hw = snd_bcm2835_playback_hw; 137 : : } 138 : : /* minimum 16 bytes alignment (for vchiq bulk transfers) */ 139 : 0 : snd_pcm_hw_constraint_step(runtime, 140 : : 0, 141 : : SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 142 : : 16); 143 : : 144 : : /* position update is in 10ms order */ 145 : 0 : snd_pcm_hw_constraint_minmax(runtime, 146 : : SNDRV_PCM_HW_PARAM_PERIOD_TIME, 147 : : 10 * 1000, UINT_MAX); 148 : : 149 : 0 : chip->alsa_stream[idx] = alsa_stream; 150 : : 151 : 0 : chip->opened |= (1 << idx); 152 : : 153 : : out: 154 : 0 : mutex_unlock(&chip->audio_mutex); 155 : : 156 : 0 : return err; 157 : : } 158 : : 159 : 0 : static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) 160 : : { 161 : 0 : return snd_bcm2835_playback_open_generic(substream, 0); 162 : : } 163 : : 164 : 0 : static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream) 165 : : { 166 : 0 : return snd_bcm2835_playback_open_generic(substream, 1); 167 : : } 168 : : 169 : 0 : static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) 170 : : { 171 : : struct bcm2835_alsa_stream *alsa_stream; 172 : : struct snd_pcm_runtime *runtime; 173 : : struct bcm2835_chip *chip; 174 : : 175 : 0 : chip = snd_pcm_substream_chip(substream); 176 : 0 : mutex_lock(&chip->audio_mutex); 177 : 0 : runtime = substream->runtime; 178 : 0 : alsa_stream = runtime->private_data; 179 : : 180 : 0 : alsa_stream->period_size = 0; 181 : 0 : alsa_stream->buffer_size = 0; 182 : : 183 : 0 : bcm2835_audio_close(alsa_stream); 184 : 0 : alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; 185 : : /* 186 : : * Do not free up alsa_stream here, it will be freed up by 187 : : * runtime->private_free callback we registered in *_open above 188 : : */ 189 : : 190 : 0 : chip->opened &= ~(1 << substream->number); 191 : : 192 : 0 : mutex_unlock(&chip->audio_mutex); 193 : : 194 : 0 : return 0; 195 : : } 196 : : 197 : 0 : static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, 198 : : struct snd_pcm_hw_params *params) 199 : : { 200 : 0 : return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); 201 : : } 202 : : 203 : 0 : static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) 204 : : { 205 : 0 : return snd_pcm_lib_free_pages(substream); 206 : : } 207 : : 208 : 0 : static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) 209 : : { 210 : 0 : struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); 211 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 212 : 0 : struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; 213 : : int channels; 214 : : int err; 215 : : 216 : : /* notify the vchiq that it should enter spdif passthrough mode by 217 : : * setting channels=0 (see 218 : : * https://github.com/raspberrypi/linux/issues/528) 219 : : */ 220 : 0 : if (chip->spdif_status & IEC958_AES0_NONAUDIO) 221 : : channels = 0; 222 : : else 223 : 0 : channels = runtime->channels; 224 : : 225 : 0 : err = bcm2835_audio_set_params(alsa_stream, channels, 226 : : runtime->rate, 227 : 0 : snd_pcm_format_width(runtime->format)); 228 : 0 : if (err < 0) 229 : : return err; 230 : : 231 : 0 : memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); 232 : : 233 : 0 : alsa_stream->pcm_indirect.hw_buffer_size = 234 : 0 : alsa_stream->pcm_indirect.sw_buffer_size = 235 : : snd_pcm_lib_buffer_bytes(substream); 236 : : 237 : 0 : alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); 238 : 0 : alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); 239 : : atomic_set(&alsa_stream->pos, 0); 240 : 0 : alsa_stream->period_offset = 0; 241 : 0 : alsa_stream->draining = false; 242 : 0 : alsa_stream->interpolate_start = ktime_get(); 243 : : 244 : 0 : return 0; 245 : : } 246 : : 247 : 0 : static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, 248 : : struct snd_pcm_indirect *rec, size_t bytes) 249 : : { 250 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 251 : 0 : struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; 252 : 0 : void *src = (void *) (substream->runtime->dma_area + rec->sw_data); 253 : : 254 : 0 : bcm2835_audio_write(alsa_stream, bytes, src); 255 : 0 : } 256 : : 257 : 0 : static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) 258 : : { 259 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 260 : 0 : struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; 261 : 0 : struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; 262 : : 263 : 0 : return snd_pcm_indirect_playback_transfer(substream, pcm_indirect, 264 : : snd_bcm2835_pcm_transfer); 265 : : } 266 : : 267 : : /* trigger callback */ 268 : 0 : static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 269 : : { 270 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 271 : 0 : struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; 272 : : 273 : 0 : switch (cmd) { 274 : : case SNDRV_PCM_TRIGGER_START: 275 : 0 : return bcm2835_audio_start(alsa_stream); 276 : : case SNDRV_PCM_TRIGGER_DRAIN: 277 : 0 : alsa_stream->draining = true; 278 : 0 : return bcm2835_audio_drain(alsa_stream); 279 : : case SNDRV_PCM_TRIGGER_STOP: 280 : 0 : return bcm2835_audio_stop(alsa_stream); 281 : : default: 282 : : return -EINVAL; 283 : : } 284 : : } 285 : : 286 : : /* pointer callback */ 287 : : static snd_pcm_uframes_t 288 : 0 : snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) 289 : : { 290 : 0 : struct snd_pcm_runtime *runtime = substream->runtime; 291 : 0 : struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; 292 : 0 : ktime_t now = ktime_get(); 293 : : 294 : : /* Give userspace better delay reporting by interpolating between GPU 295 : : * notifications, assuming audio speed is close enough to the clock 296 : : * used for ktime 297 : : */ 298 : : 299 : 0 : if ((ktime_to_ns(alsa_stream->interpolate_start)) && 300 : : (ktime_compare(alsa_stream->interpolate_start, now) < 0)) { 301 : 0 : u64 interval = 302 : 0 : (ktime_to_ns(ktime_sub(now, 303 : : alsa_stream->interpolate_start))); 304 : : u64 frames_output_in_interval = 305 : 0 : div_u64((interval * runtime->rate), 1000000000); 306 : 0 : snd_pcm_sframes_t frames_output_in_interval_sized = 307 : : -frames_output_in_interval; 308 : 0 : runtime->delay = frames_output_in_interval_sized; 309 : : } 310 : : 311 : 0 : return snd_pcm_indirect_playback_pointer(substream, 312 : : &alsa_stream->pcm_indirect, 313 : : atomic_read(&alsa_stream->pos)); 314 : : } 315 : : 316 : : /* operators */ 317 : : static const struct snd_pcm_ops snd_bcm2835_playback_ops = { 318 : : .open = snd_bcm2835_playback_open, 319 : : .close = snd_bcm2835_playback_close, 320 : : .ioctl = snd_pcm_lib_ioctl, 321 : : .hw_params = snd_bcm2835_pcm_hw_params, 322 : : .hw_free = snd_bcm2835_pcm_hw_free, 323 : : .prepare = snd_bcm2835_pcm_prepare, 324 : : .trigger = snd_bcm2835_pcm_trigger, 325 : : .pointer = snd_bcm2835_pcm_pointer, 326 : : .ack = snd_bcm2835_pcm_ack, 327 : : }; 328 : : 329 : : static const struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { 330 : : .open = snd_bcm2835_playback_spdif_open, 331 : : .close = snd_bcm2835_playback_close, 332 : : .ioctl = snd_pcm_lib_ioctl, 333 : : .hw_params = snd_bcm2835_pcm_hw_params, 334 : : .hw_free = snd_bcm2835_pcm_hw_free, 335 : : .prepare = snd_bcm2835_pcm_prepare, 336 : : .trigger = snd_bcm2835_pcm_trigger, 337 : : .pointer = snd_bcm2835_pcm_pointer, 338 : : .ack = snd_bcm2835_pcm_ack, 339 : : }; 340 : : 341 : : /* create a pcm device */ 342 : 3 : int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, const char *name, 343 : : int idx, enum snd_bcm2835_route route, 344 : : u32 numchannels, bool spdif) 345 : : { 346 : : struct snd_pcm *pcm; 347 : : int err; 348 : : 349 : 3 : err = snd_pcm_new(chip->card, name, idx, numchannels, 0, &pcm); 350 : 3 : if (err) 351 : : return err; 352 : : 353 : 3 : pcm->private_data = chip; 354 : 3 : pcm->nonatomic = true; 355 : 3 : strcpy(pcm->name, name); 356 : 3 : if (!spdif) { 357 : 3 : chip->dest = route; 358 : 3 : chip->volume = 0; 359 : 3 : chip->mute = CTRL_VOL_UNMUTE; 360 : : } 361 : : 362 : 3 : snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 363 : : spdif ? &snd_bcm2835_playback_spdif_ops : 364 : : &snd_bcm2835_playback_ops); 365 : : 366 : 3 : snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, 367 : 3 : chip->card->dev, 128 * 1024, 128 * 1024); 368 : : 369 : 3 : if (spdif) 370 : 0 : chip->pcm_spdif = pcm; 371 : : else 372 : 3 : chip->pcm = pcm; 373 : : return 0; 374 : : }