Глава 13. Управление питанием

Предыдущая  Содержание  Следующая V*D*V

Если чип допускает работу с функциями приостановки/возобновления работы, необходимо добавить в драйвер код управления питанием. Дополнительный код для управления питанием должен использовать ifdef с CONFIG_PM.

 

Если драйвер полностью поддерживает приостановку/возобновление, то есть устройство может правильно восстановить своё состояние после вызова приостановки, можно установить в поле PCM info флаг SNDRV_PCM_INFO_RESUME. Как правило, это возможно, если регистры чипа могут быть безопасно сохранены и восстановлены из ОЗУ. Если флаг установлен, после завершения обратного вызова resume вызывается обратный вызов trigger с SNDRV_PCM_TRIGGER_RESUME.

 

Даже если драйвер не поддерживает полностью управление питанием, но частичная приостановка/возобновление всё же возможна, обратные вызовы приостановки/возобновления по-прежнему стоит реализовать. В таком случае приложения сбрасывали бы состояние вызывая snd_pcm_prepare() и надлежащим образом перезапускали поток. Таким образом, можно в дальнейшем определить обратные вызовы приостановки/возобновления, но не устанавливать в PCM в поле info флаг SNDRV_PCM_INFO_RESUME.

 

Обратите внимание, что независимо от флага SNDRV_PCM_INFO_RESUME, если  вызвана snd_pcm_suspend_all, всегда может быть вызван trigger с SUSPEND. Флаг RESUME влияет только на поведение snd_pcm_resume(). (Таким образом, в теории, когда флаг SNDRV_PCM_INFO_RESUME не установлен, в обратном вызове trigger нет необходимости обрабатывать SNDRV_PCM_TRIGGER_RESUME. Но лучше сохранить его по соображениям совместимости.)

 

В более ранней версии драйверов ALSA предоставлялся универсальный уровень управления питанием, но он был удалён. Драйвер должен определить обработку приостановки/возобновления в соответствии с шиной, к которой подключено устройство. В случае драйверов PCI обратные вызовы выглядят следующим образом:

 

#ifdef CONFIG_PM

static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)

{

    .... /* делаем что-нибудь для приостановки */

    return 0;

}

static int snd_my_resume(struct pci_dev *pci)

{

    .... /* делаем что-нибудь для возобновления */

    return 0;

}

#endif

 

Последовательность реальной приостановки работы следующая:

 

1.Получить данные карты и чипа.

2.Вызвать snd_power_change_state() с SNDRV_CTL_POWER_D3hot для изменения состояния питания.

3.Вызвать snd_pcm_suspend_all(), чтобы приостановить работающие потоки PCM.

4.Если используются кодеки AC97, вызвать для каждого кодека snd_ac97_suspend().

5.Сохранить значения регистров, если необходимо.

6.Остановить оборудования, если это необходимо.

7.Отключить устройство PCI вызовом pci_disable_device(). Затем, наконец, вызвать pci_save_state().

 

Типичный код будет таким:

 

static int mychip_suspend(struct pci_dev *pci, pm_message_t state)

{

    /* (1) */

    struct snd_card *card = pci_get_drvdata(pci);

    struct mychip *chip = card->private_data;

    /* (2) */

    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);

    /* (3) */

    snd_pcm_suspend_all(chip->pcm);

    /* (4) */

    snd_ac97_suspend(chip->ac97);

    /* (5) */

    snd_mychip_save_registers(chip);

    /* (6) */

    snd_mychip_stop_hardware(chip);

    /* (7) */

    pci_disable_device(pci);

    pci_save_state(pci);

    return 0;

}

 

Последовательность реального возобновления работы следующая:

 

1.Получить данные карты и чипа.

2.Настроить PCI. Во-первых, вызвать pci_restore_state(). Затем снова включить устройство PCI, вызвав pci_enable_device().  При необходимости также вызвать pci_set_master().

3.Переинициализировать чип.

4.Восстановить сохранённые регистры, если необходимо.

5.Возобновить работу микшера, вызвав, например, snd_ac97_resume().

6.Перезапустить оборудование (если это надо).

7.Вызвать snd_power_change_state() с SNDRV_CTL_POWER_D0 для уведомления процессов.

 

Типичный код будет таким:

 

static int mychip_resume(struct pci_dev *pci)

{

    /* (1) */

    struct snd_card *card = pci_get_drvdata(pci);

    struct mychip *chip = card->private_data;

    /* (2) */

    pci_restore_state(pci);

    pci_enable_device(pci);

    pci_set_master(pci);

    /* (3) */

    snd_mychip_reinit_chip(chip);

    /* (4) */

    snd_mychip_restore_registers(chip);

    /* (5) */

    snd_ac97_resume(chip->ac97);

    /* (6) */

    snd_mychip_restart_chip(chip);

    /* (7) */

    snd_power_change_state(card, SNDRV_CTL_POWER_D0);

    return 0;

}

 

Как показано выше, лучше сохранять регистры после приостановления операций PCM через snd_pcm_suspend_all() или snd_pcm_suspend(). Это означает, что при получении снимка состояния регистров потоки PCM уже остановлены. Но помните, что  в обратном вызове resume нет необходимости перезапускать PCM поток. Он будет перезапущен через вызов trigger с SNDRV_PCM_TRIGGER_RESUME, когда это необходимо.

 

Хорошо, сейчас у нас есть все обратные вызовы. Давайте установим их. При инициализации карты убедитесь, что вы можете получить данные чипа из экземпляра карты, обычно через поле private_data, в случае, если вы самостоятельно создали объект чипа.

 

static int __devinit snd_mychip_probe(struct pci_dev *pci,

                        const struct pci_device_id *pci_id)

{

    ....

    struct snd_card *card;

    struct mychip *chip;

    int err;

    ....

    err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);

    ....

    chip = kzalloc(sizeof(*chip), GFP_KERNEL);

    ....

    card->private_data = chip;

    ....

}

 

Если объект чипа создан с помощью snd_card_create(), он всё равно доступен через поле private_data.

 

static int __devinit snd_mychip_probe(struct pci_dev *pci,

                        const struct pci_device_id *pci_id)

{

    ....

    struct snd_card *card;

    struct mychip *chip;

    int err;

    ....

    err = snd_card_create(index[dev], id[dev], THIS_MODULE,

                          sizeof(struct mychip), &card);

    ....

    chip = card->private_data;

    ....

}

 

Если необходимо место для сохранения регистров, также выделите буфер для этого здесь, поскольку было бы смертельным, если вы бы не смогли выделить память в фазе приостановки работы. Выделенный буфер должен быть освобождён в соответствующем деструкторе.

 

И, наконец, устанавливаем функции обратного вызова приостановки/возобновления в pci_driver.

 

static struct pci_driver driver = {

    .name     = "My Chip",

    .id_table = snd_my_ids,

    .probe    = snd_my_probe,

    .remove   = __devexit_p(snd_my_remove),

#ifdef CONFIG_PM

    .suspend  = snd_my_suspend,

    .resume   = snd_my_resume,

#endif

};

 

Предыдущая  Содержание  Следующая