Merge branch 'topic/timestamp' into for-next
authorTakashi Iwai <tiwai@suse.de>
Mon, 23 Feb 2015 08:15:02 +0000 (09:15 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 23 Feb 2015 08:15:02 +0000 (09:15 +0100)
1  2 
include/uapi/sound/asound.h
sound/pci/hda/hda_controller.c

@@@ -25,9 -25,6 +25,9 @@@
  
  #include <linux/types.h>
  
 +#ifndef __KERNEL__
 +#include <stdlib.h>
 +#endif
  
  /*
   *  protocol version
@@@ -143,7 -140,7 +143,7 @@@ struct snd_hwdep_dsp_image 
   *                                                                           *
   *****************************************************************************/
  
- #define SNDRV_PCM_VERSION             SNDRV_PROTOCOL_VERSION(2, 0, 12)
+ #define SNDRV_PCM_VERSION             SNDRV_PROTOCOL_VERSION(2, 0, 13)
  
  typedef unsigned long snd_pcm_uframes_t;
  typedef signed long snd_pcm_sframes_t;
@@@ -270,10 -267,17 +270,17 @@@ typedef int __bitwise snd_pcm_subformat
  #define SNDRV_PCM_INFO_JOINT_DUPLEX   0x00200000      /* playback and capture stream are somewhat correlated */
  #define SNDRV_PCM_INFO_SYNC_START     0x00400000      /* pcm support some kind of sync go */
  #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP       0x00800000      /* period wakeup can be disabled */
- #define SNDRV_PCM_INFO_HAS_WALL_CLOCK   0x01000000      /* has audio wall clock for audio/system time sync */
+ #define SNDRV_PCM_INFO_HAS_WALL_CLOCK   0x01000000      /* (Deprecated)has audio wall clock for audio/system time sync */
+ #define SNDRV_PCM_INFO_HAS_LINK_ATIME              0x01000000  /* report hardware link audio time, reset on startup */
+ #define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME     0x02000000  /* report absolute hardware link audio time, not reset on startup */
+ #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME    0x04000000  /* report estimated link audio time */
+ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000  /* report synchronized audio/system time */
  #define SNDRV_PCM_INFO_DRAIN_TRIGGER  0x40000000              /* internal kernel flag - trigger in drain */
  #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000      /* internal kernel flag - FIFO size is in frames */
  
  typedef int __bitwise snd_pcm_state_t;
  #define       SNDRV_PCM_STATE_OPEN            ((__force snd_pcm_state_t) 0) /* stream is open */
  #define       SNDRV_PCM_STATE_SETUP           ((__force snd_pcm_state_t) 1) /* stream has a setup */
@@@ -411,6 -415,22 +418,22 @@@ struct snd_pcm_channel_info 
        unsigned int step;              /* samples distance in bits */
  };
  
+ enum {
+       /*
+        *  first definition for backwards compatibility only,
+        *  maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else
+        */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0,
+       /* timestamp definitions */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1,           /* DMA time, reported as per hw_ptr */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2,              /* link time reported by sample or wallclock counter, reset on startup */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3,     /* link time reported by sample or wallclock counter, not reset on startup */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4,    /* link time estimated indirectly */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */
+       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
+ };
  struct snd_pcm_status {
        snd_pcm_state_t state;          /* stream state */
        struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */
        snd_pcm_uframes_t avail_max;    /* max frames available on hw since last status */
        snd_pcm_uframes_t overrange;    /* count of ADC (capture) overrange detections from last status */
        snd_pcm_state_t suspended_state; /* suspended stream state */
-       __u32 reserved_alignment;       /* must be filled with zero */
-       struct timespec audio_tstamp;   /* from sample counter or wall clock */
-       unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */
+       __u32 audio_tstamp_data;         /* needed for 64-bit alignment, used for configs/report to/from userspace */
+       struct timespec audio_tstamp;   /* sample counter, wall clock, PHC or on-demand sync'ed */
+       struct timespec driver_tstamp;  /* useful in case reference system tstamp is reported with delay */
+       __u32 audio_tstamp_accuracy;    /* in ns units, only valid if indicated in audio_tstamp_data */
+       unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */
  };
  
  struct snd_pcm_mmap_status {
@@@ -537,6 -559,7 +562,7 @@@ enum 
  #define SNDRV_PCM_IOCTL_DELAY         _IOR('A', 0x21, snd_pcm_sframes_t)
  #define SNDRV_PCM_IOCTL_HWSYNC                _IO('A', 0x22)
  #define SNDRV_PCM_IOCTL_SYNC_PTR      _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
+ #define SNDRV_PCM_IOCTL_STATUS_EXT    _IOWR('A', 0x24, struct snd_pcm_status)
  #define SNDRV_PCM_IOCTL_CHANNEL_INFO  _IOR('A', 0x32, struct snd_pcm_channel_info)
  #define SNDRV_PCM_IOCTL_PREPARE               _IO('A', 0x40)
  #define SNDRV_PCM_IOCTL_RESET         _IO('A', 0x41)
@@@ -30,6 -30,7 +30,6 @@@
  #include <linux/reboot.h>
  #include <sound/core.h>
  #include <sound/initval.h>
 -#include "hda_priv.h"
  #include "hda_controller.h"
  
  #define CREATE_TRACE_POINTS
@@@ -731,17 -732,32 +731,32 @@@ static snd_pcm_uframes_t azx_pcm_pointe
                               azx_get_position(chip, azx_dev));
  }
  
- static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
-                               struct timespec *ts)
+ static int azx_get_time_info(struct snd_pcm_substream *substream,
+                       struct timespec *system_ts, struct timespec *audio_ts,
+                       struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+                       struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
  {
        struct azx_dev *azx_dev = get_azx_dev(substream);
        u64 nsec;
  
-       nsec = timecounter_read(&azx_dev->azx_tc);
-       nsec = div_u64(nsec, 3); /* can be optimized */
-       nsec = azx_adjust_codec_delay(substream, nsec);
+       if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
+               (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
  
-       *ts = ns_to_timespec(nsec);
+               snd_pcm_gettime(substream->runtime, system_ts);
+               nsec = timecounter_read(&azx_dev->azx_tc);
+               nsec = div_u64(nsec, 3); /* can be optimized */
+               if (audio_tstamp_config->report_delay)
+                       nsec = azx_adjust_codec_delay(substream, nsec);
+               *audio_ts = ns_to_timespec(nsec);
+               audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+               audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
+               audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
+       } else
+               audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
  
        return 0;
  }
@@@ -755,7 -771,8 +770,8 @@@ static struct snd_pcm_hardware azx_pcm_
                                 /* SNDRV_PCM_INFO_RESUME |*/
                                 SNDRV_PCM_INFO_PAUSE |
                                 SNDRV_PCM_INFO_SYNC_START |
-                                SNDRV_PCM_INFO_HAS_WALL_CLOCK |
+                                SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
+                                SNDRV_PCM_INFO_HAS_LINK_ATIME |
                                 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
        .formats =              SNDRV_PCM_FMTBIT_S16_LE,
        .rates =                SNDRV_PCM_RATE_48000,
@@@ -841,10 -858,12 +857,12 @@@ static int azx_pcm_open(struct snd_pcm_
                return -EINVAL;
        }
  
-       /* disable WALLCLOCK timestamps for capture streams
+       /* disable LINK_ATIME timestamps for capture streams
           until we figure out how to handle digital inputs */
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK;
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
+               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
+       }
  
        spin_lock_irqsave(&chip->reg_lock, flags);
        azx_dev->substream = substream;
@@@ -876,7 -895,7 +894,7 @@@ static struct snd_pcm_ops azx_pcm_ops 
        .prepare = azx_pcm_prepare,
        .trigger = azx_pcm_trigger,
        .pointer = azx_pcm_pointer,
-       .wall_clock =  azx_get_wallclock_tstamp,
+       .get_time_info =  azx_get_time_info,
        .mmap = azx_pcm_mmap,
        .page = snd_pcm_sgbuf_ops_page,
  };
@@@ -1680,7 -1699,7 +1698,7 @@@ irqreturn_t azx_interrupt(int irq, voi
        int i;
  
  #ifdef CONFIG_PM
 -      if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
 +      if (azx_has_pm_runtime(chip))
                if (!pm_runtime_active(chip->card->dev))
                        return IRQ_NONE;
  #endif
@@@ -1783,7 -1802,7 +1801,7 @@@ static void azx_power_notify(struct hda
  {
        struct azx *chip = bus->private_data;
  
 -      if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
 +      if (!azx_has_pm_runtime(chip))
                return;
  
        if (power_up)
@@@ -1814,65 -1833,41 +1832,65 @@@ static int get_jackpoll_interval(struc
        return j;
  }
  
 -/* Codec initialization */
 -int azx_codec_create(struct azx *chip, const char *model,
 -                   unsigned int max_slots,
 -                   int *power_save_to)
 -{
 -      struct hda_bus_template bus_temp;
 -      int c, codecs, err;
 -
 -      memset(&bus_temp, 0, sizeof(bus_temp));
 -      bus_temp.private_data = chip;
 -      bus_temp.modelname = model;
 -      bus_temp.pci = chip->pci;
 -      bus_temp.ops.command = azx_send_cmd;
 -      bus_temp.ops.get_response = azx_get_response;
 -      bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
 -      bus_temp.ops.bus_reset = azx_bus_reset;
 +static struct hda_bus_ops bus_ops = {
 +      .command = azx_send_cmd,
 +      .get_response = azx_get_response,
 +      .attach_pcm = azx_attach_pcm_stream,
 +      .bus_reset = azx_bus_reset,
  #ifdef CONFIG_PM
 -      bus_temp.power_save = power_save_to;
 -      bus_temp.ops.pm_notify = azx_power_notify;
 +      .pm_notify = azx_power_notify,
  #endif
  #ifdef CONFIG_SND_HDA_DSP_LOADER
 -      bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare;
 -      bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger;
 -      bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup;
 +      .load_dsp_prepare = azx_load_dsp_prepare,
 +      .load_dsp_trigger = azx_load_dsp_trigger,
 +      .load_dsp_cleanup = azx_load_dsp_cleanup,
  #endif
 +};
  
 -      err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
 +/* HD-audio bus initialization */
 +int azx_bus_create(struct azx *chip, const char *model, int *power_save_to)
 +{
 +      struct hda_bus *bus;
 +      int err;
 +
 +      err = snd_hda_bus_new(chip->card, &bus);
        if (err < 0)
                return err;
  
 +      chip->bus = bus;
 +      bus->private_data = chip;
 +      bus->pci = chip->pci;
 +      bus->modelname = model;
 +      bus->ops = bus_ops;
 +#ifdef CONFIG_PM
 +      bus->power_save = power_save_to;
 +#endif
 +
        if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
                dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
 -              chip->bus->needs_damn_long_delay = 1;
 +              bus->needs_damn_long_delay = 1;
 +      }
 +
 +      /* AMD chipsets often cause the communication stalls upon certain
 +       * sequence like the pin-detection.  It seems that forcing the synced
 +       * access works around the stall.  Grrr...
 +       */
 +      if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
 +              dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n");
 +              bus->sync_write = 1;
 +              bus->allow_bus_reset = 1;
        }
  
 +      return 0;
 +}
 +EXPORT_SYMBOL_GPL(azx_bus_create);
 +
 +/* Probe codecs */
 +int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
 +{
 +      struct hda_bus *bus = chip->bus;
 +      int c, codecs, err;
 +
        codecs = 0;
        if (!max_slots)
                max_slots = AZX_DEFAULT_CODECS;
                }
        }
  
 -      /* AMD chipsets often cause the communication stalls upon certain
 -       * sequence like the pin-detection.  It seems that forcing the synced
 -       * access works around the stall.  Grrr...
 -       */
 -      if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
 -              dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n");
 -              chip->bus->sync_write = 1;
 -              chip->bus->allow_bus_reset = 1;
 -      }
 -
        /* Then create codec instances */
        for (c = 0; c < max_slots; c++) {
                if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
                        struct hda_codec *codec;
 -                      err = snd_hda_codec_new(chip->bus, c, &codec);
 +                      err = snd_hda_codec_new(bus, c, &codec);
                        if (err < 0)
                                continue;
                        codec->jackpoll_interval = get_jackpoll_interval(chip);
        }
        return 0;
  }
 -EXPORT_SYMBOL_GPL(azx_codec_create);
 +EXPORT_SYMBOL_GPL(azx_probe_codecs);
  
  /* configure each codec instance */
  int azx_codec_configure(struct azx *chip)
  }
  EXPORT_SYMBOL_GPL(azx_codec_configure);
  
 -/* mixer creation - all stuff is implemented in hda module */
 -int azx_mixer_create(struct azx *chip)
 -{
 -      return snd_hda_build_controls(chip->bus);
 -}
 -EXPORT_SYMBOL_GPL(azx_mixer_create);
 -
  
  static bool is_input_stream(struct azx *chip, unsigned char index)
  {