1284778Sdelphij// SPDX-License-Identifier: GPL-2.0-or-later 2267843Sdelphij/* 3267843Sdelphij * Power management for audio on multifunction CS5535 companion device 4284778Sdelphij * Copyright (C) Jaya Kumar 5267843Sdelphij */ 6267843Sdelphij 7267843Sdelphij#include <linux/init.h> 8267843Sdelphij#include <linux/pci.h> 9267843Sdelphij#include <linux/delay.h> 10267843Sdelphij#include <sound/core.h> 11267843Sdelphij#include <sound/control.h> 12267843Sdelphij#include <sound/initval.h> 13267843Sdelphij#include <sound/asoundef.h> 14267843Sdelphij#include <sound/pcm.h> 15267843Sdelphij#include <sound/ac97_codec.h> 16267843Sdelphij#include "cs5535audio.h" 17284778Sdelphij 18284778Sdelphijstatic void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) 19284778Sdelphij{ 20284778Sdelphij /* 21284778Sdelphij we depend on snd_ac97_suspend to tell the 22284778Sdelphij AC97 codec to shutdown. the amd spec suggests 23284778Sdelphij that the LNK_SHUTDOWN be done at the same time 24284778Sdelphij that the codec power-down is issued. instead, 25284778Sdelphij we do it just after rather than at the same 26284778Sdelphij time. excluding codec specific build_ops->suspend 27284778Sdelphij ac97 powerdown hits: 28267843Sdelphij 0x8000 EAPD 29267843Sdelphij 0x4000 Headphone amplifier 30267843Sdelphij 0x0300 ADC & DAC 31267843Sdelphij 0x0400 Analog Mixer powerdown (Vref on) 32267843Sdelphij I am not sure if this is the best that we can do. 33267843Sdelphij The remainder to be investigated are: 34267843Sdelphij - analog mixer (vref off) 0x0800 35267843Sdelphij - AC-link powerdown 0x1000 36267843Sdelphij - codec internal clock 0x2000 37267843Sdelphij */ 38267843Sdelphij 39267843Sdelphij /* set LNK_SHUTDOWN to shutdown AC link */ 40267843Sdelphij cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN); 41267843Sdelphij 42267843Sdelphij} 43267843Sdelphij 44267843Sdelphijstatic int __maybe_unused snd_cs5535audio_suspend(struct device *dev) 45267843Sdelphij{ 46267843Sdelphij struct snd_card *card = dev_get_drvdata(dev); 47267843Sdelphij struct cs5535audio *cs5535au = card->private_data; 48267843Sdelphij int i; 49267843Sdelphij 50267843Sdelphij snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 51267843Sdelphij snd_ac97_suspend(cs5535au->ac97); 52267843Sdelphij for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { 53267843Sdelphij struct cs5535audio_dma *dma = &cs5535au->dmas[i]; 54267843Sdelphij if (dma && dma->substream) 55267843Sdelphij dma->saved_prd = dma->ops->read_prd(cs5535au); 56267843Sdelphij } 57267843Sdelphij /* save important regs, then disable aclink in hw */ 58267843Sdelphij snd_cs5535audio_stop_hardware(cs5535au); 59267843Sdelphij return 0; 60267843Sdelphij} 61267843Sdelphij 62267843Sdelphijstatic int __maybe_unused snd_cs5535audio_resume(struct device *dev) 63267843Sdelphij{ 64267843Sdelphij struct snd_card *card = dev_get_drvdata(dev); 65267843Sdelphij struct cs5535audio *cs5535au = card->private_data; 66267843Sdelphij u32 tmp; 67267843Sdelphij int timeout; 68267843Sdelphij int i; 69267843Sdelphij 70267843Sdelphij /* set LNK_WRM_RST to reset AC link */ 71267843Sdelphij cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST); 72267843Sdelphij 73267843Sdelphij timeout = 50; 74267843Sdelphij do { 75267843Sdelphij tmp = cs_readl(cs5535au, ACC_CODEC_STATUS); 76267843Sdelphij if (tmp & PRM_RDY_STS) 77267843Sdelphij break; 78267843Sdelphij udelay(1); 79267843Sdelphij } while (--timeout); 80267843Sdelphij 81267843Sdelphij if (!timeout) 82267843Sdelphij dev_err(cs5535au->card->dev, "Failure getting AC Link ready\n"); 83267843Sdelphij 84267843Sdelphij /* set up rate regs, dma. actual initiation is done in trig */ 85267843Sdelphij for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { 86267843Sdelphij struct cs5535audio_dma *dma = &cs5535au->dmas[i]; 87267843Sdelphij if (dma && dma->substream) { 88267843Sdelphij dma->substream->ops->prepare(dma->substream); 89267843Sdelphij dma->ops->setup_prd(cs5535au, dma->saved_prd); 90267843Sdelphij } 91267843Sdelphij } 92267843Sdelphij 93267843Sdelphij /* we depend on ac97 to perform the codec power up */ 94267843Sdelphij snd_ac97_resume(cs5535au->ac97); 95267843Sdelphij snd_power_change_state(card, SNDRV_CTL_POWER_D0); 96267843Sdelphij 97284778Sdelphij return 0; 98267843Sdelphij} 99267843Sdelphij 100267843SdelphijSIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume); 101267843Sdelphij