root/drivers/media/pci/cobalt/cobalt-alsa-pcm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sample_cpy
  2. cobalt_alsa_announce_pcm_data
  3. alsa_fnc
  4. snd_cobalt_pcm_capture_open
  5. snd_cobalt_pcm_capture_close
  6. snd_cobalt_pcm_ioctl
  7. snd_pcm_alloc_vmalloc_buffer
  8. snd_cobalt_pcm_hw_params
  9. snd_cobalt_pcm_hw_free
  10. snd_cobalt_pcm_prepare
  11. snd_cobalt_pcm_trigger
  12. snd_cobalt_pcm_pointer
  13. pb_sample_cpy
  14. cobalt_alsa_pb_pcm_data
  15. alsa_pb_fnc
  16. snd_cobalt_pcm_playback_open
  17. snd_cobalt_pcm_playback_close
  18. snd_cobalt_pcm_pb_prepare
  19. snd_cobalt_pcm_pb_trigger
  20. snd_cobalt_pcm_pb_pointer
  21. snd_pcm_get_vmalloc_page
  22. snd_cobalt_pcm_create

   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 }

/* [<][>][^][v][top][bottom][index][help] */