Глава 7. API для кодека AC97 |
Предыдущая Содержание Следующая |
Общие сведенияУровень кодека 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 имеют свои собственные функции для чтения частоты. Файлы интерфейса ProcALSA AC97 интерфейс создаёт файлы proc, такие как /proc/asound/card0/codec97#0/ac97#0-0 и ac97#0-0+regs. Можно обращаться к этим файлам, чтобы увидеть текущее состояние и регистры кодека. Несколько кодековЕсли на одной карте есть несколько кодеков, необходимо вызывать snd_ac97_mixer() несколько раз с ac97.num=1 или большим значением. Поле num указывается количество кодеков.
Если указано несколько кодеков, необходимо либо писать разные функции обратного вызова для каждого кодека, либо в процедурах обратного вызова проверять ac97->num.
|
Предыдущая Содержание Следующая |