Глава 7. API для кодека AC97

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

Общие сведения

Уровень кодека AC97 в ALSA чётко определён, и вам не придётся писать много кода, чтобы им управлять. Необходимы только процедуры низкоуровневого управления. API кодека AC97 определён в <sound/ac97_codec.h>.

Полный пример кода

Пример 7.1. Пример интерфейса AC97

 

struct mychip {

    ....

    struct snd_ac97 *ac97;

    ....

};

 

static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,

                                           unsigned short reg)

{

    struct mychip *chip = ac97->private_data;

    ....

    /* здесь из кодека читаем значение регистра */

    return the_register_value;

}

 

static void snd_mychip_ac97_write(struct snd_ac97 *ac97,

                                  unsigned short reg, unsigned short val)

{

    struct mychip *chip = ac97->private_data;

    ....

    /* записываем в кодек значение данного регистра */

}

 

static int snd_mychip_ac97(struct mychip *chip)

{

    struct snd_ac97_bus *bus;

    struct snd_ac97_template ac97;

    int err;

    static struct snd_ac97_bus_ops ops = {

        .write = snd_mychip_ac97_write,

        .read = snd_mychip_ac97_read,

    };

 

    err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);

    if (err < 0)

        return err;

    memset(&ac97, 0, sizeof(ac97));

    ac97.private_data = chip;

    return snd_ac97_mixer(bus, &ac97, &chip->ac97);

}

 

Конструктор

Чтобы создать экземпляр AC97, сначала вызывается snd_ac97_bus используя объект ac97_bus_ops_t, содержащий функции обратного вызова.

 

struct snd_ac97_bus *bus;

static struct snd_ac97_bus_ops ops = {

    .write = snd_mychip_ac97_write,

    .read = snd_mychip_ac97_read,

};

 

snd_ac97_bus(card, 0, &ops, NULL, &pbus);

 

Объект bus является общим объектом для всего, связанного с экземплярами AC97.

 

А затем вызывается snd_ac97_mixer() с объектом struct snd_ac97_template вместе с указателем bus, созданным до этого.

 

struct snd_ac97_template ac97;

int err;

 

memset(&ac97, 0, sizeof(ac97));

ac97.private_data = chip;

snd_ac97_mixer(bus, &ac97, &chip->ac97);

 

где chip->ac97 является указателем на вновь созданный экземпляр ac97_t. В данном случае указатель chip сохраняется в  закрытых данных, так что функции обратного вызова для чтения/записи могут обращаться к этому экземпляру chip. Экземпляр ac97 не обязательно хранить в объекте chip. Если вам необходимо изменять значения регистров из драйвера или необходимо приостанавливать/возобновлять работу кодеков AC97, сохраните этот указатель для передачи соответствующим функциям.

Обратные вызовы

Стандартными обратными вызовами являются read и write. Очевидно, что они соответствуют низкоуровневым функциям обращения к оборудованию для чтения и записи.

 

Обратный вызов read возвращает значение регистра, указанного в аргументе.

 

static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,

                                           unsigned short reg)

{

    struct mychip *chip = ac97->private_data;

    ....

    return the_register_value;

}

 

Здесь chip может быть получен приведением ac97->private_data.

 

Обратный же вызов write используется для установки значения регистра.

 

static void snd_mychip_ac97_write(struct snd_ac97 *ac97,

                                  unsigned short reg, unsigned short val)

 

Эти обратные вызовы не являются атомарными, подобно обратным вызовам API управления.

 

Есть также другие функции обратного вызова: reset, wait и init.

 

Обратный вызов reset используется для сброса кодека. Если чип требует особый способ сброса, можно определить этот обратный вызов.

 

Обратный вызов wait используется для добавления некоторого времени ожидания в стандартную инициализацию кодека. Если чип требует дополнительного времени ожидания, определите этот обратный вызов.

 

Обратный вызов init используется для дополнительной инициализации кодека.

Обновление регистров в драйвере

Если необходимо обращаться к кодеку из драйвера, можно вызывать следующие функции: snd_ac97_write(), snd_ac97_read (), snd_ac97_update() и snd_ac97_update_bits().

 

Обе функции, snd_ac97_write() и snd_ac97_update(), используются для установки значения указанного регистра (AC97_XXX). Разница между ними в том, что snd_ac97_update() не записывает значение, если данное значение уже установлено, а snd_ac97_write() переписывает значение всегда.

 

snd_ac97_write(ac97, AC97_MASTER, 0x8080);

snd_ac97_update(ac97, AC97_MASTER, 0x8080);

 

snd_ac97_read() используется для чтения значения заданного регистра. Например,

 

value = snd_ac97_read(ac97, AC97_MASTER);

 

snd_ac97_update_bits() используется для обновления некоторых битов в заданном регистре.

 

snd_ac97_update_bits(ac97, reg, mask, value);

 

Кроме того, есть функция изменения частоты дискретизации (значение в указанном регистре, таком как AC97_PCM_FRONT_DAC_RATE), если кодеком поддерживается VRA (Variable Rate PCM audio, звук с PCM и изменяемой частотой дискретизации) или DRA (Double-Rate PCM audio, звук с PCM с удвоенной частотой дискретизации): snd_ac97_set_rate().

 

snd_ac97_set_rate(ac97, AC97_PCM_FRONT_DAC_RATE, 44100);

 

Для установки частоты дискретизации доступны следующие регистры: AC97_PCM_MIC_ADC_RATE, AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE, AC97_SPDIF. Если указан AC97_SPDIF, регистр на самом деле не изменяется, а будут обновляться соответствующие биты статуса IEC958.

Регулировка тактовой частоты

В некоторых чипах частота кодека не 48000, а используется частота PCI (для экономии кварца!). В этом случае измените поле bus->clock на соответствующее значение. Например, драйверы intel8x0 и es1968 имеют свои собственные функции для чтения частоты.

Файлы интерфейса Proc

ALSA AC97 интерфейс создаёт файлы proc, такие как /proc/asound/card0/codec97#0/ac97#0-0 и ac97#0-0+regs. Можно обращаться к этим файлам, чтобы увидеть текущее состояние и регистры кодека.

Несколько кодеков

Если на одной карте есть несколько кодеков, необходимо вызывать snd_ac97_mixer() несколько раз с ac97.num=1 или большим значением. Поле num указывается количество кодеков.

 

Если указано несколько кодеков, необходимо либо писать разные функции обратного вызова для каждого кодека, либо в процедурах обратного вызова проверять ac97->num.

 

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