1308 lines · 34.3 KB · C
RawDownload
// 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.