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