mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
ALSA: hda: Move irq pending work into hda-intel stream
Currently, the delayed IRQ handling for PCM streams is managed in a single work embedded in hda_intel, but this is basically a per-stream thing. Due to the single work, we can't cancel the work properly at closing each stream, for example. For making the IRQ pending work to be stream-based, this patch changes the following: - An extended version of azx_dev (i.e. the hd-audio stream object) is defined for snd-hda-intel - The irq_pending flag and irq_pending_work are moved to hda_intel_stream, so that they can be hda-intel stream specific - The stream creation and assignment are refactored so that snd-hda-intel can handle individually; the snd-hda-intel specific workaround for stream tags is also moved to snd-hda-intel itself instead of the common code - The irq pending work is canceled properly at free / shutdown While we're at it, changed the bit field flag to bool, as the bit field doesn't help much in our case. Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20260519121157.28477-1-tiwai@suse.de
This commit is contained in:
parent
b59d5c51bb
commit
e36a88b33c
|
|
@ -1264,19 +1264,17 @@ int azx_codec_configure(struct azx *chip)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(azx_codec_configure);
|
||||
|
||||
static int stream_direction(struct azx *chip, unsigned char index)
|
||||
void azx_add_stream(struct azx *chip, struct azx_dev *azx_dev, int idx, int tag)
|
||||
{
|
||||
if (index >= chip->capture_index_offset &&
|
||||
index < chip->capture_index_offset + chip->capture_streams)
|
||||
return SNDRV_PCM_STREAM_CAPTURE;
|
||||
return SNDRV_PCM_STREAM_PLAYBACK;
|
||||
snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev), idx,
|
||||
azx_stream_direction(chip, idx), tag);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(azx_add_stream);
|
||||
|
||||
/* initialize SD streams */
|
||||
int azx_init_streams(struct azx *chip)
|
||||
{
|
||||
int i;
|
||||
int stream_tags[2] = { 0, 0 };
|
||||
|
||||
/* initialize each stream (aka device)
|
||||
* assign the starting bdl address to each stream (device)
|
||||
|
|
@ -1284,24 +1282,10 @@ int azx_init_streams(struct azx *chip)
|
|||
*/
|
||||
for (i = 0; i < chip->num_streams; i++) {
|
||||
struct azx_dev *azx_dev = kzalloc_obj(*azx_dev);
|
||||
int dir, tag;
|
||||
|
||||
if (!azx_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dir = stream_direction(chip, i);
|
||||
/* stream tag must be unique throughout
|
||||
* the stream direction group,
|
||||
* valid values 1...15
|
||||
* use separate stream tag if the flag
|
||||
* AZX_DCAPS_SEPARATE_STREAM_TAG is used
|
||||
*/
|
||||
if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
|
||||
tag = ++stream_tags[dir];
|
||||
else
|
||||
tag = i + 1;
|
||||
snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev),
|
||||
i, dir, tag);
|
||||
azx_add_stream(chip, azx_dev, i, i + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -57,13 +57,12 @@ enum {
|
|||
struct azx_dev {
|
||||
struct hdac_stream core;
|
||||
|
||||
unsigned int irq_pending:1;
|
||||
/*
|
||||
* For VIA:
|
||||
* A flag to ensure DMA position is 0
|
||||
* when link position is not greater than FIFO size
|
||||
*/
|
||||
unsigned int insufficient:1;
|
||||
bool insufficient;
|
||||
};
|
||||
|
||||
#define azx_stream(dev) (&(dev)->core)
|
||||
|
|
@ -206,6 +205,15 @@ int azx_bus_init(struct azx *chip, const char *model);
|
|||
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
|
||||
int azx_codec_configure(struct azx *chip);
|
||||
int azx_init_streams(struct azx *chip);
|
||||
void azx_add_stream(struct azx *chip, struct azx_dev *s, int idx, int tag);
|
||||
void azx_free_streams(struct azx *chip);
|
||||
|
||||
static inline int azx_stream_direction(struct azx *chip, unsigned char index)
|
||||
{
|
||||
if (index >= chip->capture_index_offset &&
|
||||
index < chip->capture_index_offset + chip->capture_streams)
|
||||
return SNDRV_PCM_STREAM_CAPTURE;
|
||||
return SNDRV_PCM_STREAM_PLAYBACK;
|
||||
}
|
||||
|
||||
#endif /* __SOUND_HDA_CONTROLLER_H */
|
||||
|
|
|
|||
|
|
@ -615,17 +615,17 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
|
|||
/* called from IRQ */
|
||||
static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
|
||||
{
|
||||
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
|
||||
struct hda_intel_stream *istream = azx_dev_to_istream(azx_dev);
|
||||
int ok;
|
||||
|
||||
ok = azx_position_ok(chip, azx_dev);
|
||||
if (ok == 1) {
|
||||
azx_dev->irq_pending = 0;
|
||||
istream->irq_pending = false;
|
||||
return ok;
|
||||
} else if (ok == 0) {
|
||||
/* bogus IRQ, process it later */
|
||||
azx_dev->irq_pending = 1;
|
||||
schedule_work(&hda->irq_pending_work);
|
||||
istream->irq_pending = true;
|
||||
schedule_work(&istream->irq_pending_work);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -721,11 +721,13 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
|
|||
*/
|
||||
static void azx_irq_pending_work(struct work_struct *work)
|
||||
{
|
||||
struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work);
|
||||
struct hda_intel_stream *istream =
|
||||
container_of(work, struct hda_intel_stream, irq_pending_work);
|
||||
struct azx_dev *azx_dev = &istream->azx_dev;
|
||||
struct hda_intel *hda = istream->hda;
|
||||
struct azx *chip = &hda->chip;
|
||||
struct hdac_bus *bus = azx_bus(chip);
|
||||
struct hdac_stream *s;
|
||||
int pending, ok;
|
||||
int ok;
|
||||
|
||||
if (!hda->irq_pending_warned) {
|
||||
dev_info(chip->card->dev,
|
||||
|
|
@ -735,28 +737,25 @@ static void azx_irq_pending_work(struct work_struct *work)
|
|||
}
|
||||
|
||||
for (;;) {
|
||||
pending = 0;
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
struct azx_dev *azx_dev = stream_to_azx_dev(s);
|
||||
if (!azx_dev->irq_pending ||
|
||||
!s->substream ||
|
||||
!s->running)
|
||||
continue;
|
||||
scoped_guard(spinlock_irq, &bus->reg_lock) {
|
||||
if (!istream->irq_pending ||
|
||||
!azx_dev->core.substream ||
|
||||
!azx_dev->core.running) {
|
||||
return;
|
||||
}
|
||||
|
||||
ok = azx_position_ok(chip, azx_dev);
|
||||
if (ok > 0) {
|
||||
azx_dev->irq_pending = 0;
|
||||
spin_unlock(&bus->reg_lock);
|
||||
snd_pcm_period_elapsed(s->substream);
|
||||
spin_lock(&bus->reg_lock);
|
||||
} else if (ok < 0) {
|
||||
pending = 0; /* too early */
|
||||
} else
|
||||
pending++;
|
||||
if (ok < 0)
|
||||
return; /* too early */
|
||||
if (ok > 0)
|
||||
istream->irq_pending = false;
|
||||
}
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
if (!pending)
|
||||
|
||||
if (ok) {
|
||||
snd_pcm_period_elapsed(azx_dev->core.substream);
|
||||
return;
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -767,10 +766,11 @@ static void azx_clear_irq_pending(struct azx *chip)
|
|||
struct hdac_bus *bus = azx_bus(chip);
|
||||
struct hdac_stream *s;
|
||||
|
||||
guard(spinlock_irq)(&bus->reg_lock);
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
struct azx_dev *azx_dev = stream_to_azx_dev(s);
|
||||
azx_dev->irq_pending = 0;
|
||||
struct hda_intel_stream *istream = azx_dev_to_istream(azx_dev);
|
||||
istream->irq_pending = false;
|
||||
cancel_work_sync(&istream->irq_pending_work);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1797,7 +1797,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000)
|
||||
chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]);
|
||||
INIT_LIST_HEAD(&chip->pcm_list);
|
||||
INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
|
||||
INIT_LIST_HEAD(&hda->list);
|
||||
init_vga_switcheroo(chip);
|
||||
init_completion(&hda->probe_wait);
|
||||
|
|
@ -1846,6 +1845,39 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* create and assign streams */
|
||||
static int hda_init_streams(struct azx *chip)
|
||||
{
|
||||
int i;
|
||||
int stream_tags[2] = { 0, 0 };
|
||||
|
||||
for (i = 0; i < chip->num_streams; i++) {
|
||||
struct hda_intel_stream *s = kzalloc_obj(*s);
|
||||
int tag, dir;
|
||||
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
s->hda = container_of(chip, struct hda_intel, chip);
|
||||
INIT_WORK(&s->irq_pending_work, azx_irq_pending_work);
|
||||
|
||||
/* stream tag must be unique throughout
|
||||
* the stream direction group,
|
||||
* valid values 1...15
|
||||
* use separate stream tag if the flag
|
||||
* AZX_DCAPS_SEPARATE_STREAM_TAG is used
|
||||
*/
|
||||
dir = azx_stream_direction(chip, i);
|
||||
if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
|
||||
tag = ++stream_tags[dir];
|
||||
else
|
||||
tag = i + 1;
|
||||
azx_add_stream(chip, &s->azx_dev, i, tag);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int azx_first_init(struct azx *chip)
|
||||
{
|
||||
int dev = chip->dev_index;
|
||||
|
|
@ -2000,7 +2032,7 @@ static int azx_first_init(struct azx *chip)
|
|||
}
|
||||
|
||||
/* initialize streams */
|
||||
err = azx_init_streams(chip);
|
||||
err = hda_init_streams(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@
|
|||
struct hda_intel {
|
||||
struct azx chip;
|
||||
|
||||
/* for pending irqs */
|
||||
struct work_struct irq_pending_work;
|
||||
|
||||
/* sync probing */
|
||||
struct completion probe_wait;
|
||||
struct delayed_work probe_work;
|
||||
|
|
@ -35,4 +32,16 @@ struct hda_intel {
|
|||
int probe_retry; /* being probe-retry */
|
||||
};
|
||||
|
||||
struct hda_intel_stream {
|
||||
struct azx_dev azx_dev;
|
||||
|
||||
/* for pending irqs */
|
||||
struct hda_intel *hda;
|
||||
struct work_struct irq_pending_work;
|
||||
bool irq_pending;
|
||||
};
|
||||
|
||||
#define azx_dev_to_istream(azx_dev) \
|
||||
container_of(azx_dev, struct hda_intel_stream, azx_dev)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user