#include <linux/types.h>
+#ifndef __KERNEL__
+#include <stdlib.h>
+#endif
/*
* protocol version
* *
*****************************************************************************/
- #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;
#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 */
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 {
#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)
#include <linux/reboot.h>
#include <sound/core.h>
#include <sound/initval.h>
-#include "hda_priv.h"
#include "hda_controller.h"
#define CREATE_TRACE_POINTS
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;
}
/* 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,
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;
.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,
};
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
{
struct azx *chip = bus->private_data;
- if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
+ if (!azx_has_pm_runtime(chip))
return;
if (power_up)
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)
{