Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/pci/cobalt/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 12 kB image not shown  

Quelle  cobalt-alsa-pcm.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  ALSA PCM device for the
 *  ALSA interface to cobalt PCM capture streams
 *
 *  Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
 *  All rights reserved.
 */


#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>

#include <media/v4l2-device.h>

#include <sound/core.h>
#include <sound/pcm.h>

#include "cobalt-driver.h"
#include "cobalt-alsa.h"
#include "cobalt-alsa-pcm.h"

static unsigned int pcm_debug;
module_param(pcm_debug, int, 0644);
MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");

#define dprintk(fmt, arg...) \
 do { \
  if (pcm_debug) \
   pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
 } while (0)

static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
  SNDRV_PCM_INFO_MMAP           |
  SNDRV_PCM_INFO_INTERLEAVED    |
  SNDRV_PCM_INFO_MMAP_VALID,

 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,

 .rates = SNDRV_PCM_RATE_48000,

 .rate_min = 48000,
 .rate_max = 48000,
 .channels_min = 1,
 .channels_max = 8,
 .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */
 .period_bytes_min = 1920,  /* 1 sample = 8 * 4 bytes */
 .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */
 .periods_min = 1,
 .periods_max = 4,
};

static const struct snd_pcm_hardware snd_cobalt_playback = {
 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
  SNDRV_PCM_INFO_MMAP           |
  SNDRV_PCM_INFO_INTERLEAVED    |
  SNDRV_PCM_INFO_MMAP_VALID,

 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,

 .rates = SNDRV_PCM_RATE_48000,

 .rate_min = 48000,
 .rate_max = 48000,
 .channels_min = 1,
 .channels_max = 8,
 .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */
 .period_bytes_min = 1920,  /* 1 sample = 8 * 4 bytes */
 .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */
 .periods_min = 1,
 .periods_max = 4,
};

static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
{
 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
 unsigned idx = 0;

 while (len >= (is_s32 ? 4 : 2)) {
  unsigned offset = map[idx] * 4;
  u32 val = src[offset + 1] + (src[offset + 2] << 8) +
    (src[offset + 3] << 16);

  if (is_s32) {
   *dst++ = 0;
   *dst++ = val & 0xff;
  }
  *dst++ = (val >> 8) & 0xff;
  *dst++ = (val >> 16) & 0xff;
  len -= is_s32 ? 4 : 2;
  idx++;
 }
}

static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
     u8 *pcm_data,
     size_t skip,
     size_t samples)
{
 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 unsigned long flags;
 unsigned int oldptr;
 unsigned int stride;
 int length = samples;
 int period_elapsed = 0;
 bool is_s32;

 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
  pcm_data, samples);

 substream = cobsc->capture_pcm_substream;
 if (substream == NULL) {
  dprintk("substream was NULL\n");
  return;
 }

 runtime = substream->runtime;
 if (runtime == NULL) {
  dprintk("runtime was NULL\n");
  return;
 }
 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;

 stride = runtime->frame_bits >> 3;
 if (stride == 0) {
  dprintk("stride is zero\n");
  return;
 }

 if (length == 0) {
  dprintk("%s: length was zero\n", __func__);
  return;
 }

 if (runtime->dma_area == NULL) {
  dprintk("dma area was NULL - ignoring\n");
  return;
 }

 oldptr = cobsc->hwptr_done_capture;
 if (oldptr + length >= runtime->buffer_size) {
  unsigned int cnt = runtime->buffer_size - oldptr;
  unsigned i;

  for (i = 0; i < cnt; i++)
   sample_cpy(runtime->dma_area + (oldptr + i) * stride,
     pcm_data + i * skip,
     stride, is_s32);
  for (i = cnt; i < length; i++)
   sample_cpy(runtime->dma_area + (i - cnt) * stride,
     pcm_data + i * skip, stride, is_s32);
 } else {
  unsigned i;

  for (i = 0; i < length; i++)
   sample_cpy(runtime->dma_area + (oldptr + i) * stride,
     pcm_data + i * skip,
     stride, is_s32);
 }
 snd_pcm_stream_lock_irqsave(substream, flags);

 cobsc->hwptr_done_capture += length;
 if (cobsc->hwptr_done_capture >=
     runtime->buffer_size)
  cobsc->hwptr_done_capture -=
   runtime->buffer_size;

 cobsc->capture_transfer_done += length;
 if (cobsc->capture_transfer_done >=
     runtime->period_size) {
  cobsc->capture_transfer_done -=
   runtime->period_size;
  period_elapsed = 1;
 }

 snd_pcm_stream_unlock_irqrestore(substream, flags);

 if (period_elapsed)
  snd_pcm_period_elapsed(substream);
}

static int alsa_fnc(struct vb2_buffer *vb, void *priv)
{
 struct cobalt_stream *s = priv;
 unsigned char *p = vb2_plane_vaddr(vb, 0);
 int i;

 if (pcm_debug) {
  pr_info("alsa: ");
  for (i = 0; i < 8 * 4; i++) {
   if (!(i & 3))
    pr_cont(" ");
   pr_cont("%02x", p[i]);
  }
  pr_cont("\n");
 }
 cobalt_alsa_announce_pcm_data(s->alsa,
   vb2_plane_vaddr(vb, 0),
   8 * 4,
   vb2_get_plane_payload(vb, 0) / (8 * 4));
 return 0;
}

static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
 struct cobalt_stream *s = cobsc->s;

 runtime->hw = snd_cobalt_hdmi_capture;
 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 cobsc->capture_pcm_substream = substream;
 runtime->private_data = s;
 cobsc->alsa_record_cnt++;
 if (cobsc->alsa_record_cnt == 1) {
  int rc;

  rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
  if (rc) {
   cobsc->alsa_record_cnt--;
   return rc;
  }
 }
 return 0;
}

static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
 struct cobalt_stream *s = cobsc->s;

 cobsc->alsa_record_cnt--;
 if (cobsc->alsa_record_cnt == 0)
  vb2_thread_stop(&s->q);
 return 0;
}

static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);

 cobsc->hwptr_done_capture = 0;
 cobsc->capture_transfer_done = 0;

 return 0;
}

static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
 switch (cmd) {
 case SNDRV_PCM_TRIGGER_START:
 case SNDRV_PCM_TRIGGER_STOP:
  return 0;
 default:
  return -EINVAL;
 }
 return 0;
}

static
snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
{
 snd_pcm_uframes_t hwptr_done;
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);

 hwptr_done = cobsc->hwptr_done_capture;

 return hwptr_done;
}

static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
{
 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
 unsigned idx = 0;

 while (len >= (is_s32 ? 4 : 2)) {
  unsigned offset = map[idx] * 4;
  u8 *out = dst + offset;

  *out++ = 0;
  if (is_s32) {
   src++;
   *out++ = *src++;
  } else {
   *out++ = 0;
  }
  *out++ = *src++;
  *out = *src++;
  len -= is_s32 ? 4 : 2;
  idx++;
 }
}

static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
     u8 *pcm_data,
     size_t skip,
     size_t samples)
{
 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 unsigned long flags;
 unsigned int pos;
 unsigned int stride;
 bool is_s32;
 unsigned i;

 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
  pcm_data, samples);

 substream = cobsc->playback_pcm_substream;
 if (substream == NULL) {
  dprintk("substream was NULL\n");
  return;
 }

 runtime = substream->runtime;
 if (runtime == NULL) {
  dprintk("runtime was NULL\n");
  return;
 }

 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
 stride = runtime->frame_bits >> 3;
 if (stride == 0) {
  dprintk("stride is zero\n");
  return;
 }

 if (samples == 0) {
  dprintk("%s: samples was zero\n", __func__);
  return;
 }

 if (runtime->dma_area == NULL) {
  dprintk("dma area was NULL - ignoring\n");
  return;
 }

 pos = cobsc->pb_pos % cobsc->pb_size;
 for (i = 0; i < cobsc->pb_count / (8 * 4); i++)
  pb_sample_cpy(pcm_data + i * skip,
    runtime->dma_area + pos + i * stride,
    stride, is_s32);
 snd_pcm_stream_lock_irqsave(substream, flags);

 cobsc->pb_pos += i * stride;

 snd_pcm_stream_unlock_irqrestore(substream, flags);
 if (cobsc->pb_pos % cobsc->pb_count == 0)
  snd_pcm_period_elapsed(substream);
}

static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
{
 struct cobalt_stream *s = priv;

 if (s->alsa->alsa_pb_channel)
  cobalt_alsa_pb_pcm_data(s->alsa,
    vb2_plane_vaddr(vb, 0),
    8 * 4,
    vb2_get_plane_payload(vb, 0) / (8 * 4));
 return 0;
}

static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct cobalt_stream *s = cobsc->s;

 runtime->hw = snd_cobalt_playback;
 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 cobsc->playback_pcm_substream = substream;
 runtime->private_data = s;
 cobsc->alsa_playback_cnt++;
 if (cobsc->alsa_playback_cnt == 1) {
  int rc;

  rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
  if (rc) {
   cobsc->alsa_playback_cnt--;
   return rc;
  }
 }

 return 0;
}

static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
 struct cobalt_stream *s = cobsc->s;

 cobsc->alsa_playback_cnt--;
 if (cobsc->alsa_playback_cnt == 0)
  vb2_thread_stop(&s->q);
 return 0;
}

static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);

 cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream);
 cobsc->pb_count = snd_pcm_lib_period_bytes(substream);
 cobsc->pb_pos = 0;

 return 0;
}

static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
         int cmd)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);

 switch (cmd) {
 case SNDRV_PCM_TRIGGER_START:
  if (cobsc->alsa_pb_channel)
   return -EBUSY;
  cobsc->alsa_pb_channel = true;
  return 0;
 case SNDRV_PCM_TRIGGER_STOP:
  cobsc->alsa_pb_channel = false;
  return 0;
 default:
  return -EINVAL;
 }
}

static
snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
{
 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
 size_t ptr;

 ptr = cobsc->pb_pos;

 return bytes_to_frames(substream->runtime, ptr) %
        substream->runtime->buffer_size;
}

static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = {
 .open  = snd_cobalt_pcm_capture_open,
 .close  = snd_cobalt_pcm_capture_close,
 .prepare = snd_cobalt_pcm_prepare,
 .trigger = snd_cobalt_pcm_trigger,
 .pointer = snd_cobalt_pcm_pointer,
};

static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = {
 .open  = snd_cobalt_pcm_playback_open,
 .close  = snd_cobalt_pcm_playback_close,
 .prepare = snd_cobalt_pcm_pb_prepare,
 .trigger = snd_cobalt_pcm_pb_trigger,
 .pointer = snd_cobalt_pcm_pb_pointer,
};

int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
{
 struct snd_pcm *sp;
 struct snd_card *sc = cobsc->sc;
 struct cobalt_stream *s = cobsc->s;
 struct cobalt *cobalt = s->cobalt;
 int ret;

 s->q.gfp_flags |= __GFP_ZERO;

 if (!s->is_output) {
  cobalt_s_bit_sysctrl(cobalt,
   COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
   0);
  mdelay(2);
  cobalt_s_bit_sysctrl(cobalt,
   COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
   1);
  mdelay(1);

  ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
   0, /* PCM device 0, the only one for this card */
   0, /* 0 playback substreams */
   1, /* 1 capture substream */
   &sp);
  if (ret) {
   cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
       ret);
   goto err_exit;
  }

  snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
    &snd_cobalt_pcm_capture_ops);
  snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
            NULL, 0, 0);
  sp->info_flags = 0;
  sp->private_data = cobsc;
  strscpy(sp->name, "cobalt"sizeof(sp->name));
 } else {
  cobalt_s_bit_sysctrl(cobalt,
   COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
  mdelay(2);
  cobalt_s_bit_sysctrl(cobalt,
   COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1);
  mdelay(1);

  ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
   0, /* PCM device 0, the only one for this card */
   1, /* 0 playback substreams */
   0, /* 1 capture substream */
   &sp);
  if (ret) {
   cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
       ret);
   goto err_exit;
  }

  snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
    &snd_cobalt_pcm_playback_ops);
  snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
            NULL, 0, 0);
  sp->info_flags = 0;
  sp->private_data = cobsc;
  strscpy(sp->name, "cobalt"sizeof(sp->name));
 }

 return 0;

err_exit:
 return ret;
}

Messung V0.5
C=98 H=90 G=94

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.