// SPDX-License-Identifier: GPL-2.0-or-later/* * * Implementation of primary alsa driver code base for Intel HD Audio. * * Copyright(c) 2004 Intel Corporation * * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> * PeiSen Hou <pshou@realtek.com.tw> */ #include <linux/clocksource.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/pm_runtime.h>#include <linux/slab.h> #ifdef CONFIG_X86/* for art-tsc conversion */#include <asm/tsc.h>#endif #include <sound/core.h>#include <sound/initval.h>#include <sound/pcm_params.h>#include "hda_controller.h"#include "hda_local.h" #define CREATE_TRACE_POINTS#include "controller_trace.h" /* DSP lock helpers */#ifdef CONFIG_SND_HDA_DSP_LOADER#define guard_dsp_lock(dev) guard(snd_hdac_dsp_lock)(azx_stream(dev))#else#define guard_dsp_lock(dev) do {} while (0)#endif#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev)) /* assign a stream for the PCM */static inline struct azx_dev *azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream){ struct hdac_stream *s; s = snd_hdac_stream_assign(azx_bus(chip), substream); if (!s) return NULL; return stream_to_azx_dev(s);} /* release the assigned stream */static inline void azx_release_device(struct azx_dev *azx_dev){ snd_hdac_stream_release(azx_stream(azx_dev));} static inline struct hda_pcm_stream *to_hda_pcm_stream(struct snd_pcm_substream *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); return &apcm->info->stream[substream->stream];} static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, u64 nsec){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); u64 codec_frames, codec_nsecs; if (!hinfo->ops.get_delay) return nsec; codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream); codec_nsecs = div_u64(codec_frames * 1000000000LL, substream->runtime->rate); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) return nsec + codec_nsecs; return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;} /* * PCM ops */ static int azx_pcm_close(struct snd_pcm_substream *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); trace_azx_pcm_close(chip, azx_dev); scoped_guard(mutex, &chip->open_mutex) { if (chip->ops->pcm_close) chip->ops->pcm_close(chip, azx_dev); azx_release_device(azx_dev); if (hinfo->ops.close) hinfo->ops.close(hinfo, apcm->codec, substream); snd_hda_power_down(apcm->codec); } snd_hda_codec_pcm_put(apcm->info); return 0;} static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); struct hdac_stream *hdas = azx_stream(azx_dev); trace_azx_pcm_hw_params(chip, azx_dev); guard_dsp_lock(azx_dev); if (dsp_is_locked(azx_dev)) return -EBUSY; /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ hdas->bufsize = params_buffer_bytes(hw_params); hdas->period_bytes = params_period_bytes(hw_params); hdas->format_val = 0; hdas->no_period_wakeup = (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); if (snd_hdac_stream_setup_periods(hdas) < 0) return -ENOMEM; return 0;} static int azx_pcm_hw_free(struct snd_pcm_substream *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); /* reset BDL address */ guard_dsp_lock(azx_dev); if (!dsp_is_locked(azx_dev)) snd_hdac_stream_cleanup(azx_stream(azx_dev)); snd_hda_codec_cleanup(apcm->codec, hinfo, substream); azx_stream(azx_dev)->prepared = 0; return 0;} static int azx_pcm_prepare(struct snd_pcm_substream *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int format_val, stream_tag, bits; int err; struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); unsigned short ctls = spdif ? spdif->ctls : 0; trace_azx_pcm_prepare(chip, azx_dev); guard_dsp_lock(azx_dev); if (dsp_is_locked(azx_dev)) return -EBUSY; snd_hdac_stream_reset(azx_stream(azx_dev)); bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps); format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls); if (!format_val) { dev_err(chip->card->dev, "invalid format_val, rate=%d, ch=%d, format=%d\n", runtime->rate, runtime->channels, runtime->format); return -EINVAL; } err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val); if (err < 0) return err; snd_hdac_stream_setup(azx_stream(azx_dev), false); stream_tag = azx_dev->core.stream_tag; /* CA-IBG chips need the playback stream starting from 1 */ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) && stream_tag > chip->capture_streams) stream_tag -= chip->capture_streams; err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, azx_dev->core.format_val, substream); if (err < 0) return err; azx_stream(azx_dev)->prepared = 1; return 0;} static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct hdac_bus *bus = azx_bus(chip); struct azx_dev *azx_dev; struct snd_pcm_substream *s; struct hdac_stream *hstr; bool start; int sbits = 0; int sync_reg; azx_dev = get_azx_dev(substream); trace_azx_pcm_trigger(chip, azx_dev, cmd); hstr = azx_stream(azx_dev); if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) sync_reg = AZX_REG_OLD_SSYNC; else sync_reg = AZX_REG_SSYNC; if (dsp_is_locked(azx_dev) || !hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = true; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = false; break; default: return -EINVAL; } snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); sbits |= 1 << azx_dev->core.index; snd_pcm_trigger_done(s, substream); } scoped_guard(spinlock, &bus->reg_lock) { /* first, set SYNC bits of corresponding streams */ snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); if (start) { azx_dev->insufficient = 1; snd_hdac_stream_start(azx_stream(azx_dev)); } else { snd_hdac_stream_stop(azx_stream(azx_dev)); } } } snd_hdac_stream_sync(hstr, start, sbits); guard(spinlock)(&bus->reg_lock); /* reset SYNC bits */ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); snd_hdac_stream_timecounter_init(hstr, sbits, start); return 0;} unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev){ return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));}EXPORT_SYMBOL_GPL(azx_get_pos_lpib); unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev){ return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev));}EXPORT_SYMBOL_GPL(azx_get_pos_posbuf); unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev){ struct snd_pcm_substream *substream = azx_dev->core.substream; unsigned int pos; int stream = substream->stream; int delay = 0; if (chip->get_position[stream]) pos = chip->get_position[stream](chip, azx_dev); else /* use the position buffer as default */ pos = azx_get_pos_posbuf(chip, azx_dev); if (pos >= azx_dev->core.bufsize) pos = 0; if (substream->runtime) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); if (chip->get_delay[stream]) delay += chip->get_delay[stream](chip, azx_dev, pos); if (hinfo->ops.get_delay) delay += hinfo->ops.get_delay(hinfo, apcm->codec, substream); substream->runtime->delay = delay; } trace_azx_get_position(chip, azx_dev, pos, delay); return pos;}EXPORT_SYMBOL_GPL(azx_get_position); static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream){ struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); return bytes_to_frames(substream->runtime, azx_get_position(chip, azx_dev));} /* * azx_scale64: Scale base by mult/div while not overflowing sanely * * Derived from scale64_check_overflow in kernel/time/timekeeping.c * * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which * is about 384307 ie ~4.5 days. * * This scales the calculation so that overflow will happen but after 2^64 / * 48000 secs, which is pretty large! * * In caln below: * base may overflow, but since there isn’t any additional division * performed on base it’s OK * rem can’t overflow because both are 32-bit values */ #ifdef CONFIG_X86static u64 azx_scale64(u64 base, u32 num, u32 den){ u64 rem; rem = do_div(base, den); base *= num; rem *= num; do_div(rem, den); return base + rem;} static int azx_get_sync_time(ktime_t *device, struct system_counterval_t *system, void *ctx){ struct snd_pcm_substream *substream = ctx; struct azx_dev *azx_dev = get_azx_dev(substream); struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct snd_pcm_runtime *runtime; u64 ll_counter, ll_counter_l, ll_counter_h; u64 tsc_counter, tsc_counter_l, tsc_counter_h; u32 wallclk_ctr, wallclk_cycles; bool direction; u32 dma_select; u32 timeout; u32 retry_count = 0; runtime = substream->runtime; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) direction = 1; else direction = 0; /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */ do { timeout = 100; dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) | (azx_dev->core.stream_tag - 1); snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select); /* Enable the capture */ snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK); while (timeout) { if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) & GTSCC_TSCCD_MASK) break; timeout--; } if (!timeout) { dev_err(chip->card->dev, "GTSCC capture Timedout!\n"); return -EIO; } /* Read wall clock counter */ wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC); /* Read TSC counter */ tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL); tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU); /* Read Link counter */ ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL); ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU); /* Ack: registers read done */ snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT); tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) | tsc_counter_l; ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l; wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK; /* * An error occurs near frame "rollover". The clocks in * frame value indicates whether this error may have * occurred. Here we use the value of 10 i.e., * HDA_MAX_CYCLE_OFFSET */ if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET && wallclk_cycles > HDA_MAX_CYCLE_OFFSET) break; /* * Sleep before we read again, else we may again get * value near to MAX_CYCLE. Try to sleep for different * amount of time so we dont hit the same number again */ udelay(retry_count++); } while (retry_count != HDA_MAX_CYCLE_READ_RETRY); if (retry_count == HDA_MAX_CYCLE_READ_RETRY) { dev_err_ratelimited(chip->card->dev, "Error in WALFCC cycle count\n"); return -EIO; } *device = ns_to_ktime(azx_scale64(ll_counter, NSEC_PER_SEC, runtime->rate)); *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) / ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate)); system->cycles = tsc_counter; system->cs_id = CSID_X86_ART; return 0;} #elsestatic int azx_get_sync_time(ktime_t *device, struct system_counterval_t *system, void *ctx){ return -ENXIO;}#endif static int azx_get_crosststamp(struct snd_pcm_substream *substream, struct system_device_crosststamp *xtstamp){ return get_device_system_crosststamp(azx_get_sync_time, substream, NULL, xtstamp);} static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime, struct snd_pcm_audio_tstamp_config *ts){ if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME) if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED) return true; return false;} static int azx_get_time_info(struct snd_pcm_substream *substream, struct timespec64 *system_ts, struct timespec64 *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report){ struct system_device_crosststamp xtstamp = { .clock_id = CLOCK_REALTIME }; struct azx_dev *azx_dev = get_azx_dev(substream); struct snd_pcm_runtime *runtime = substream->runtime; int ret; u64 nsec; if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&Lines 1–500 of 1308. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * * Implementation of primary alsa driver code base for Intel HD Audio. * * Copyright(c) 2004 Intel Corporation * * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> * PeiSen Hou <pshou@realtek.com.tw> */ #include <linux/clocksource.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #ifdef CONFIG_X86 /* for art-tsc conversion */ #include <asm/tsc.h> #endif #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm_params.h> #include "hda_controller.h" #include "hda_local.h" #define CREATE_TRACE_POINTS #include "controller_trace.h" /* DSP lock helpers */ #ifdef CONFIG_SND_HDA_DSP_LOADER #define guard_dsp_lock(dev) guard(snd_hdac_dsp_lock)(azx_stream(dev)) #else #define guard_dsp_lock(dev) do {} while (0) #endif #define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev)) /* assign a stream for the PCM */ static inline struct azx_dev * azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) { struct hdac_stream *s; s = snd_hdac_stream_assign(azx_bus(chip), substream); if (!s) return NULL; return stream_to_azx_dev(s); } /* release the assigned stream */ static inline void azx_release_device(struct azx_dev *azx_dev) { snd_hdac_stream_release(azx_stream(azx_dev)); } static inline struct hda_pcm_stream * to_hda_pcm_stream(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); return &apcm->info->stream[substream->stream]; } static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, u64 nsec) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); u64 codec_frames, codec_nsecs; if (!hinfo->ops.get_delay) return nsec; codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream); codec_nsecs = div_u64(codec_frames * 1000000000LL, substream->runtime->rate); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) return nsec + codec_nsecs; return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0; } /* * PCM ops */ static int azx_pcm_close(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); trace_azx_pcm_close(chip, azx_dev); scoped_guard(mutex, &chip->open_mutex) { if (chip->ops->pcm_close) chip->ops->pcm_close(chip, azx_dev); azx_release_device(azx_dev); if (hinfo->ops.close) hinfo->ops.close(hinfo, apcm->codec, substream); snd_hda_power_down(apcm->codec); } snd_hda_codec_pcm_put(apcm->info); return 0; } static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); struct hdac_stream *hdas = azx_stream(azx_dev); trace_azx_pcm_hw_params(chip, azx_dev); guard_dsp_lock(azx_dev); if (dsp_is_locked(azx_dev)) return -EBUSY; /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ hdas->bufsize = params_buffer_bytes(hw_params); hdas->period_bytes = params_period_bytes(hw_params); hdas->format_val = 0; hdas->no_period_wakeup = (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); if (snd_hdac_stream_setup_periods(hdas) < 0) return -ENOMEM; return 0; } static int azx_pcm_hw_free(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); /* reset BDL address */ guard_dsp_lock(azx_dev); if (!dsp_is_locked(azx_dev)) snd_hdac_stream_cleanup(azx_stream(azx_dev)); snd_hda_codec_cleanup(apcm->codec, hinfo, substream); azx_stream(azx_dev)->prepared = 0; return 0; } static int azx_pcm_prepare(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int format_val, stream_tag, bits; int err; struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); unsigned short ctls = spdif ? spdif->ctls : 0; trace_azx_pcm_prepare(chip, azx_dev); guard_dsp_lock(azx_dev); if (dsp_is_locked(azx_dev)) return -EBUSY; snd_hdac_stream_reset(azx_stream(azx_dev)); bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps); format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls); if (!format_val) { dev_err(chip->card->dev, "invalid format_val, rate=%d, ch=%d, format=%d\n", runtime->rate, runtime->channels, runtime->format); return -EINVAL; } err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val); if (err < 0) return err; snd_hdac_stream_setup(azx_stream(azx_dev), false); stream_tag = azx_dev->core.stream_tag; /* CA-IBG chips need the playback stream starting from 1 */ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) && stream_tag > chip->capture_streams) stream_tag -= chip->capture_streams; err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, azx_dev->core.format_val, substream); if (err < 0) return err; azx_stream(azx_dev)->prepared = 1; return 0; } static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct hdac_bus *bus = azx_bus(chip); struct azx_dev *azx_dev; struct snd_pcm_substream *s; struct hdac_stream *hstr; bool start; int sbits = 0; int sync_reg; azx_dev = get_azx_dev(substream); trace_azx_pcm_trigger(chip, azx_dev, cmd); hstr = azx_stream(azx_dev); if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) sync_reg = AZX_REG_OLD_SSYNC; else sync_reg = AZX_REG_SSYNC; if (dsp_is_locked(azx_dev) || !hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = true; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = false; break; default: return -EINVAL; } snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); sbits |= 1 << azx_dev->core.index; snd_pcm_trigger_done(s, substream); } scoped_guard(spinlock, &bus->reg_lock) { /* first, set SYNC bits of corresponding streams */ snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); if (start) { azx_dev->insufficient = 1; snd_hdac_stream_start(azx_stream(azx_dev)); } else { snd_hdac_stream_stop(azx_stream(azx_dev)); } } } snd_hdac_stream_sync(hstr, start, sbits); guard(spinlock)(&bus->reg_lock); /* reset SYNC bits */ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); snd_hdac_stream_timecounter_init(hstr, sbits, start); return 0; } unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev) { return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); } EXPORT_SYMBOL_GPL(azx_get_pos_lpib); unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev) { return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev)); } EXPORT_SYMBOL_GPL(azx_get_pos_posbuf); unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev) { struct snd_pcm_substream *substream = azx_dev->core.substream; unsigned int pos; int stream = substream->stream; int delay = 0; if (chip->get_position[stream]) pos = chip->get_position[stream](chip, azx_dev); else /* use the position buffer as default */ pos = azx_get_pos_posbuf(chip, azx_dev); if (pos >= azx_dev->core.bufsize) pos = 0; if (substream->runtime) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); if (chip->get_delay[stream]) delay += chip->get_delay[stream](chip, azx_dev, pos); if (hinfo->ops.get_delay) delay += hinfo->ops.get_delay(hinfo, apcm->codec, substream); substream->runtime->delay = delay; } trace_azx_get_position(chip, azx_dev, pos, delay); return pos; } EXPORT_SYMBOL_GPL(azx_get_position); static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); return bytes_to_frames(substream->runtime, azx_get_position(chip, azx_dev)); } /* * azx_scale64: Scale base by mult/div while not overflowing sanely * * Derived from scale64_check_overflow in kernel/time/timekeeping.c * * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which * is about 384307 ie ~4.5 days. * * This scales the calculation so that overflow will happen but after 2^64 / * 48000 secs, which is pretty large! * * In caln below: * base may overflow, but since there isn’t any additional division * performed on base it’s OK * rem can’t overflow because both are 32-bit values */ #ifdef CONFIG_X86 static u64 azx_scale64(u64 base, u32 num, u32 den) { u64 rem; rem = do_div(base, den); base *= num; rem *= num; do_div(rem, den); return base + rem; } static int azx_get_sync_time(ktime_t *device, struct system_counterval_t *system, void *ctx) { struct snd_pcm_substream *substream = ctx; struct azx_dev *azx_dev = get_azx_dev(substream); struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct snd_pcm_runtime *runtime; u64 ll_counter, ll_counter_l, ll_counter_h; u64 tsc_counter, tsc_counter_l, tsc_counter_h; u32 wallclk_ctr, wallclk_cycles; bool direction; u32 dma_select; u32 timeout; u32 retry_count = 0; runtime = substream->runtime; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) direction = 1; else direction = 0; /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */ do { timeout = 100; dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) | (azx_dev->core.stream_tag - 1); snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select); /* Enable the capture */ snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK); while (timeout) { if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) & GTSCC_TSCCD_MASK) break; timeout--; } if (!timeout) { dev_err(chip->card->dev, "GTSCC capture Timedout!\n"); return -EIO; } /* Read wall clock counter */ wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC); /* Read TSC counter */ tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL); tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU); /* Read Link counter */ ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL); ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU); /* Ack: registers read done */ snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT); tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) | tsc_counter_l; ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l; wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK; /* * An error occurs near frame "rollover". The clocks in * frame value indicates whether this error may have * occurred. Here we use the value of 10 i.e., * HDA_MAX_CYCLE_OFFSET */ if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET && wallclk_cycles > HDA_MAX_CYCLE_OFFSET) break; /* * Sleep before we read again, else we may again get * value near to MAX_CYCLE. Try to sleep for different * amount of time so we dont hit the same number again */ udelay(retry_count++); } while (retry_count != HDA_MAX_CYCLE_READ_RETRY); if (retry_count == HDA_MAX_CYCLE_READ_RETRY) { dev_err_ratelimited(chip->card->dev, "Error in WALFCC cycle count\n"); return -EIO; } *device = ns_to_ktime(azx_scale64(ll_counter, NSEC_PER_SEC, runtime->rate)); *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) / ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate)); system->cycles = tsc_counter; system->cs_id = CSID_X86_ART; return 0; } #else static int azx_get_sync_time(ktime_t *device, struct system_counterval_t *system, void *ctx) { return -ENXIO; } #endif static int azx_get_crosststamp(struct snd_pcm_substream *substream, struct system_device_crosststamp *xtstamp) { return get_device_system_crosststamp(azx_get_sync_time, substream, NULL, xtstamp); } static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime, struct snd_pcm_audio_tstamp_config *ts) { if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME) if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED) return true; return false; } static int azx_get_time_info(struct snd_pcm_substream *substream, struct timespec64 *system_ts, struct timespec64 *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { struct system_device_crosststamp xtstamp = { .clock_id = CLOCK_REALTIME }; struct azx_dev *azx_dev = get_azx_dev(substream); struct snd_pcm_runtime *runtime = substream->runtime; int ret; u64 nsec; if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&