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