1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ALSA PCM device for the 4 * ALSA interface to cobalt PCM capture streams 5 * 6 * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. 7 * All rights reserved. 8 */ 9 10 #include <linux/init.h> 11 #include <linux/kernel.h> 12 #include <linux/vmalloc.h> 13 #include <linux/delay.h> 14 15 #include <media/v4l2-device.h> 16 17 #include <sound/core.h> 18 #include <sound/pcm.h> 19 20 #include "cobalt-driver.h" 21 #include "cobalt-alsa.h" 22 #include "cobalt-alsa-pcm.h" 23 24 static unsigned int pcm_debug; 25 module_param(pcm_debug, int, 0644); 26 MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); 27 28 #define dprintk(fmt, arg...) \ 29 do { \ 30 if (pcm_debug) \ 31 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \ 32 } while (0) 33 34 static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = { 35 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 36 SNDRV_PCM_INFO_MMAP | 37 SNDRV_PCM_INFO_INTERLEAVED | 38 SNDRV_PCM_INFO_MMAP_VALID, 39 40 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 41 42 .rates = SNDRV_PCM_RATE_48000, 43 44 .rate_min = 48000, 45 .rate_max = 48000, 46 .channels_min = 1, 47 .channels_max = 8, 48 .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */ 49 .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */ 50 .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */ 51 .periods_min = 1, 52 .periods_max = 4, 53 }; 54 55 static const struct snd_pcm_hardware snd_cobalt_playback = { 56 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 57 SNDRV_PCM_INFO_MMAP | 58 SNDRV_PCM_INFO_INTERLEAVED | 59 SNDRV_PCM_INFO_MMAP_VALID, 60 61 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 62 63 .rates = SNDRV_PCM_RATE_48000, 64 65 .rate_min = 48000, 66 .rate_max = 48000, 67 .channels_min = 1, 68 .channels_max = 8, 69 .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */ 70 .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */ 71 .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */ 72 .periods_min = 1, 73 .periods_max = 4, 74 }; 75 76 static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32) 77 { 78 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 }; 79 unsigned idx = 0; 80 81 while (len >= (is_s32 ? 4 : 2)) { 82 unsigned offset = map[idx] * 4; 83 u32 val = src[offset + 1] + (src[offset + 2] << 8) + 84 (src[offset + 3] << 16); 85 86 if (is_s32) { 87 *dst++ = 0; 88 *dst++ = val & 0xff; 89 } 90 *dst++ = (val >> 8) & 0xff; 91 *dst++ = (val >> 16) & 0xff; 92 len -= is_s32 ? 4 : 2; 93 idx++; 94 } 95 } 96 97 static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc, 98 u8 *pcm_data, 99 size_t skip, 100 size_t samples) 101 { 102 struct snd_pcm_substream *substream; 103 struct snd_pcm_runtime *runtime; 104 unsigned long flags; 105 unsigned int oldptr; 106 unsigned int stride; 107 int length = samples; 108 int period_elapsed = 0; 109 bool is_s32; 110 111 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc, 112 pcm_data, samples); 113 114 substream = cobsc->capture_pcm_substream; 115 if (substream == NULL) { 116 dprintk("substream was NULL\n"); 117 return; 118 } 119 120 runtime = substream->runtime; 121 if (runtime == NULL) { 122 dprintk("runtime was NULL\n"); 123 return; 124 } 125 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE; 126 127 stride = runtime->frame_bits >> 3; 128 if (stride == 0) { 129 dprintk("stride is zero\n"); 130 return; 131 } 132 133 if (length == 0) { 134 dprintk("%s: length was zero\n", __func__); 135 return; 136 } 137 138 if (runtime->dma_area == NULL) { 139 dprintk("dma area was NULL - ignoring\n"); 140 return; 141 } 142 143 oldptr = cobsc->hwptr_done_capture; 144 if (oldptr + length >= runtime->buffer_size) { 145 unsigned int cnt = runtime->buffer_size - oldptr; 146 unsigned i; 147 148 for (i = 0; i < cnt; i++) 149 sample_cpy(runtime->dma_area + (oldptr + i) * stride, 150 pcm_data + i * skip, 151 stride, is_s32); 152 for (i = cnt; i < length; i++) 153 sample_cpy(runtime->dma_area + (i - cnt) * stride, 154 pcm_data + i * skip, stride, is_s32); 155 } else { 156 unsigned i; 157 158 for (i = 0; i < length; i++) 159 sample_cpy(runtime->dma_area + (oldptr + i) * stride, 160 pcm_data + i * skip, 161 stride, is_s32); 162 } 163 snd_pcm_stream_lock_irqsave(substream, flags); 164 165 cobsc->hwptr_done_capture += length; 166 if (cobsc->hwptr_done_capture >= 167 runtime->buffer_size) 168 cobsc->hwptr_done_capture -= 169 runtime->buffer_size; 170 171 cobsc->capture_transfer_done += length; 172 if (cobsc->capture_transfer_done >= 173 runtime->period_size) { 174 cobsc->capture_transfer_done -= 175 runtime->period_size; 176 period_elapsed = 1; 177 } 178 179 snd_pcm_stream_unlock_irqrestore(substream, flags); 180 181 if (period_elapsed) 182 snd_pcm_period_elapsed(substream); 183 } 184 185 static int alsa_fnc(struct vb2_buffer *vb, void *priv) 186 { 187 struct cobalt_stream *s = priv; 188 unsigned char *p = vb2_plane_vaddr(vb, 0); 189 int i; 190 191 if (pcm_debug) { 192 pr_info("alsa: "); 193 for (i = 0; i < 8 * 4; i++) { 194 if (!(i & 3)) 195 pr_cont(" "); 196 pr_cont("%02x", p[i]); 197 } 198 pr_cont("\n"); 199 } 200 cobalt_alsa_announce_pcm_data(s->alsa, 201 vb2_plane_vaddr(vb, 0), 202 8 * 4, 203 vb2_get_plane_payload(vb, 0) / (8 * 4)); 204 return 0; 205 } 206 207 static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream) 208 { 209 struct snd_pcm_runtime *runtime = substream->runtime; 210 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 211 struct cobalt_stream *s = cobsc->s; 212 213 runtime->hw = snd_cobalt_hdmi_capture; 214 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 215 cobsc->capture_pcm_substream = substream; 216 runtime->private_data = s; 217 cobsc->alsa_record_cnt++; 218 if (cobsc->alsa_record_cnt == 1) { 219 int rc; 220 221 rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name); 222 if (rc) { 223 cobsc->alsa_record_cnt--; 224 return rc; 225 } 226 } 227 return 0; 228 } 229 230 static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream) 231 { 232 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 233 struct cobalt_stream *s = cobsc->s; 234 235 cobsc->alsa_record_cnt--; 236 if (cobsc->alsa_record_cnt == 0) 237 vb2_thread_stop(&s->q); 238 return 0; 239 } 240 241 static int snd_cobalt_pcm_ioctl(struct snd_pcm_substream *substream, 242 unsigned int cmd, void *arg) 243 { 244 return snd_pcm_lib_ioctl(substream, cmd, arg); 245 } 246 247 248 static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, 249 size_t size) 250 { 251 struct snd_pcm_runtime *runtime = subs->runtime; 252 253 dprintk("Allocating vbuffer\n"); 254 if (runtime->dma_area) { 255 if (runtime->dma_bytes > size) 256 return 0; 257 258 vfree(runtime->dma_area); 259 } 260 runtime->dma_area = vmalloc(size); 261 if (!runtime->dma_area) 262 return -ENOMEM; 263 264 runtime->dma_bytes = size; 265 266 return 0; 267 } 268 269 static int snd_cobalt_pcm_hw_params(struct snd_pcm_substream *substream, 270 struct snd_pcm_hw_params *params) 271 { 272 dprintk("%s called\n", __func__); 273 274 return snd_pcm_alloc_vmalloc_buffer(substream, 275 params_buffer_bytes(params)); 276 } 277 278 static int snd_cobalt_pcm_hw_free(struct snd_pcm_substream *substream) 279 { 280 if (substream->runtime->dma_area) { 281 dprintk("freeing pcm capture region\n"); 282 vfree(substream->runtime->dma_area); 283 substream->runtime->dma_area = NULL; 284 } 285 286 return 0; 287 } 288 289 static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream) 290 { 291 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 292 293 cobsc->hwptr_done_capture = 0; 294 cobsc->capture_transfer_done = 0; 295 296 return 0; 297 } 298 299 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 300 { 301 switch (cmd) { 302 case SNDRV_PCM_TRIGGER_START: 303 case SNDRV_PCM_TRIGGER_STOP: 304 return 0; 305 default: 306 return -EINVAL; 307 } 308 return 0; 309 } 310 311 static 312 snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream) 313 { 314 snd_pcm_uframes_t hwptr_done; 315 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 316 317 hwptr_done = cobsc->hwptr_done_capture; 318 319 return hwptr_done; 320 } 321 322 static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32) 323 { 324 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 }; 325 unsigned idx = 0; 326 327 while (len >= (is_s32 ? 4 : 2)) { 328 unsigned offset = map[idx] * 4; 329 u8 *out = dst + offset; 330 331 *out++ = 0; 332 if (is_s32) { 333 src++; 334 *out++ = *src++; 335 } else { 336 *out++ = 0; 337 } 338 *out++ = *src++; 339 *out = *src++; 340 len -= is_s32 ? 4 : 2; 341 idx++; 342 } 343 } 344 345 static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc, 346 u8 *pcm_data, 347 size_t skip, 348 size_t samples) 349 { 350 struct snd_pcm_substream *substream; 351 struct snd_pcm_runtime *runtime; 352 unsigned long flags; 353 unsigned int pos; 354 unsigned int stride; 355 bool is_s32; 356 unsigned i; 357 358 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc, 359 pcm_data, samples); 360 361 substream = cobsc->playback_pcm_substream; 362 if (substream == NULL) { 363 dprintk("substream was NULL\n"); 364 return; 365 } 366 367 runtime = substream->runtime; 368 if (runtime == NULL) { 369 dprintk("runtime was NULL\n"); 370 return; 371 } 372 373 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE; 374 stride = runtime->frame_bits >> 3; 375 if (stride == 0) { 376 dprintk("stride is zero\n"); 377 return; 378 } 379 380 if (samples == 0) { 381 dprintk("%s: samples was zero\n", __func__); 382 return; 383 } 384 385 if (runtime->dma_area == NULL) { 386 dprintk("dma area was NULL - ignoring\n"); 387 return; 388 } 389 390 pos = cobsc->pb_pos % cobsc->pb_size; 391 for (i = 0; i < cobsc->pb_count / (8 * 4); i++) 392 pb_sample_cpy(pcm_data + i * skip, 393 runtime->dma_area + pos + i * stride, 394 stride, is_s32); 395 snd_pcm_stream_lock_irqsave(substream, flags); 396 397 cobsc->pb_pos += i * stride; 398 399 snd_pcm_stream_unlock_irqrestore(substream, flags); 400 if (cobsc->pb_pos % cobsc->pb_count == 0) 401 snd_pcm_period_elapsed(substream); 402 } 403 404 static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv) 405 { 406 struct cobalt_stream *s = priv; 407 408 if (s->alsa->alsa_pb_channel) 409 cobalt_alsa_pb_pcm_data(s->alsa, 410 vb2_plane_vaddr(vb, 0), 411 8 * 4, 412 vb2_get_plane_payload(vb, 0) / (8 * 4)); 413 return 0; 414 } 415 416 static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream) 417 { 418 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 419 struct snd_pcm_runtime *runtime = substream->runtime; 420 struct cobalt_stream *s = cobsc->s; 421 422 runtime->hw = snd_cobalt_playback; 423 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 424 cobsc->playback_pcm_substream = substream; 425 runtime->private_data = s; 426 cobsc->alsa_playback_cnt++; 427 if (cobsc->alsa_playback_cnt == 1) { 428 int rc; 429 430 rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name); 431 if (rc) { 432 cobsc->alsa_playback_cnt--; 433 return rc; 434 } 435 } 436 437 return 0; 438 } 439 440 static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream) 441 { 442 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 443 struct cobalt_stream *s = cobsc->s; 444 445 cobsc->alsa_playback_cnt--; 446 if (cobsc->alsa_playback_cnt == 0) 447 vb2_thread_stop(&s->q); 448 return 0; 449 } 450 451 static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream) 452 { 453 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 454 455 cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream); 456 cobsc->pb_count = snd_pcm_lib_period_bytes(substream); 457 cobsc->pb_pos = 0; 458 459 return 0; 460 } 461 462 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream, 463 int cmd) 464 { 465 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 466 467 switch (cmd) { 468 case SNDRV_PCM_TRIGGER_START: 469 if (cobsc->alsa_pb_channel) 470 return -EBUSY; 471 cobsc->alsa_pb_channel = true; 472 return 0; 473 case SNDRV_PCM_TRIGGER_STOP: 474 cobsc->alsa_pb_channel = false; 475 return 0; 476 default: 477 return -EINVAL; 478 } 479 } 480 481 static 482 snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream) 483 { 484 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 485 size_t ptr; 486 487 ptr = cobsc->pb_pos; 488 489 return bytes_to_frames(substream->runtime, ptr) % 490 substream->runtime->buffer_size; 491 } 492 493 static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, 494 unsigned long offset) 495 { 496 void *pageptr = subs->runtime->dma_area + offset; 497 498 return vmalloc_to_page(pageptr); 499 } 500 501 static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = { 502 .open = snd_cobalt_pcm_capture_open, 503 .close = snd_cobalt_pcm_capture_close, 504 .ioctl = snd_cobalt_pcm_ioctl, 505 .hw_params = snd_cobalt_pcm_hw_params, 506 .hw_free = snd_cobalt_pcm_hw_free, 507 .prepare = snd_cobalt_pcm_prepare, 508 .trigger = snd_cobalt_pcm_trigger, 509 .pointer = snd_cobalt_pcm_pointer, 510 .page = snd_pcm_get_vmalloc_page, 511 }; 512 513 static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = { 514 .open = snd_cobalt_pcm_playback_open, 515 .close = snd_cobalt_pcm_playback_close, 516 .ioctl = snd_cobalt_pcm_ioctl, 517 .hw_params = snd_cobalt_pcm_hw_params, 518 .hw_free = snd_cobalt_pcm_hw_free, 519 .prepare = snd_cobalt_pcm_pb_prepare, 520 .trigger = snd_cobalt_pcm_pb_trigger, 521 .pointer = snd_cobalt_pcm_pb_pointer, 522 .page = snd_pcm_get_vmalloc_page, 523 }; 524 525 int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc) 526 { 527 struct snd_pcm *sp; 528 struct snd_card *sc = cobsc->sc; 529 struct cobalt_stream *s = cobsc->s; 530 struct cobalt *cobalt = s->cobalt; 531 int ret; 532 533 s->q.gfp_flags |= __GFP_ZERO; 534 535 if (!s->is_output) { 536 cobalt_s_bit_sysctrl(cobalt, 537 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel), 538 0); 539 mdelay(2); 540 cobalt_s_bit_sysctrl(cobalt, 541 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel), 542 1); 543 mdelay(1); 544 545 ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI", 546 0, /* PCM device 0, the only one for this card */ 547 0, /* 0 playback substreams */ 548 1, /* 1 capture substream */ 549 &sp); 550 if (ret) { 551 cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n", 552 ret); 553 goto err_exit; 554 } 555 556 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, 557 &snd_cobalt_pcm_capture_ops); 558 sp->info_flags = 0; 559 sp->private_data = cobsc; 560 strscpy(sp->name, "cobalt", sizeof(sp->name)); 561 } else { 562 cobalt_s_bit_sysctrl(cobalt, 563 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0); 564 mdelay(2); 565 cobalt_s_bit_sysctrl(cobalt, 566 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1); 567 mdelay(1); 568 569 ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI", 570 0, /* PCM device 0, the only one for this card */ 571 1, /* 0 playback substreams */ 572 0, /* 1 capture substream */ 573 &sp); 574 if (ret) { 575 cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n", 576 ret); 577 goto err_exit; 578 } 579 580 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK, 581 &snd_cobalt_pcm_playback_ops); 582 sp->info_flags = 0; 583 sp->private_data = cobsc; 584 strscpy(sp->name, "cobalt", sizeof(sp->name)); 585 } 586 587 return 0; 588 589 err_exit: 590 return ret; 591 } 592