내장형 Linux 드라이브 노트(20)-------오디오 서브시스템(ASOC 프레임워크)의 Codec
18024 단어 Linux 드라이브
저와 함께 교류하는 것을 환영합니다.
앞에서 말한 Machine 부분, 끼워넣는 리눅스 드라이브 노트(19) - 오디오 서브시스템(ASOC 프레임워크)의 Machine은 이제 코드의 부분을 볼 수 있다. 끼워넣는 장치의 메인보드에 있어 오디오 코드칩을 집적할 수 있다.AsoC 구조 아래의 CODEC 설비 기능과 물리적 CODEC는 machine의 제어 아래platform 장치와 연결되어 오디오의 실제 처리, 예를 들어 음성 재생(D/A), 음성 채집(A/D)과 각종 오디오 control 컨트롤의 설정을 책임진다.우리는 계속해서 uad1341을 예로 들자,Linux4.8.17: uda134x.c
static int uda134x_codec_probe(struct platform_device *pdev)
{
struct uda134x_platform_data *pd = pdev->dev.platform_data;
struct uda134x_priv *uda134x;
if (!pd) {
dev_err(&pdev->dev, "Missing L3 bitbang function
");
return -ENODEV;
}
uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL);
if (!uda134x)
return -ENOMEM;
uda134x->pd = pd;
platform_set_drvdata(pdev, uda134x);
uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,
&uda134x_regmap_config);
if (IS_ERR(uda134x->regmap))
return PTR_ERR(uda134x->regmap);
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_uda134x, &uda134x_dai, 1);// snd_soc_codec_driver snd_soc_dai_driver
}
static struct platform_driver uda134x_codec_driver = {
.driver = {
.name = "uda134x-codec",
},
.probe = uda134x_codec_probe,
.remove = uda134x_codec_remove,
};
여기에 먼저 공간을 uda1341의 개인 데이터로 신청한 다음에regmap 메커니즘을 사용하여 제어 인터페이스를 추상화하고codecdrv는 현재 제어 방식이 무엇인지 신경 쓰지 않아도 된다.regmap 메커니즘은 리눅스 3.1에 추가된 특성이다.주요 목적은 느린 I/O 구동의 중복 논리를 줄이고 일반적인 인터페이스를 제공하여 하부 하드웨어의 레지스터를 조작하는 것이다.이어서 개인 데이터를 설정하고 마지막으로 snd 를 호출합니다soc_register_codec 함수는 codec를 등록합니다.등록 시 두 가지 데이터 구조가 포함됩니다:sndsoc_codec_driver 및 sndsoc_dai_driver:
static struct snd_soc_codec_driver soc_codec_dev_uda134x = {// codec
.probe = uda134x_soc_probe,
.set_bias_level = uda134x_set_bias_level,
.suspend_bias_off = true,
.dapm_widgets = uda134x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
.dapm_routes = uda134x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
};
codec_dai와 pcm 설정 정보는 구조체 snd 를 통해soc_dai_driver 설명,dai의 능력 설명과 조작 인터페이스 포함,sndsoc_dai_driver는 최종적으로 asoc-core에 등록됩니다.
static struct snd_soc_dai_driver uda134x_dai = {
.name = "uda134x-hifi",
/* playback capabilities */
.playback = {// , 、 、 ;
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* capture capabilities */
.capture = {// , 、 、 ;
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = &uda134x_dai_ops,
};
snd_soc_dai_driver에서 그의dai 인터페이스를 설명했는데, 그 중에서.ops 필드는 매우 중요합니다.codec 를 가리킵니다.dai의 조작 함수 집합은dai의 시계 설정, 형식 설정, 하드웨어 파라미터 설정 등 리셋을 정의했다.
static const struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
.hw_params = uda134x_hw_params,//codec_dai , 、 、 , codec_dai 。
.digital_mute = uda134x_mute,
.set_sysclk = uda134x_set_dai_sysclk,
.set_fmt = uda134x_set_dai_fmt,
};
우리의 중점은 등록 함수에 있다.
int snd_soc_register_codec(struct device *dev,
const struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
struct snd_soc_dapm_context *dapm;
struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);// , snd_soc_codec
codec->component.codec = codec;// component
ret = snd_soc_component_initialize(&codec->component,
&codec_drv->component_driver, dev);//【1】
......
if (codec_drv->controls) {// , snd_soc_codec_driver
codec->component.controls = codec_drv->controls;////【2】
codec->component.num_controls = codec_drv->num_controls;
}
if (codec_drv->dapm_widgets) {
codec->component.dapm_widgets = codec_drv->dapm_widgets;
codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
}
if (codec_drv->dapm_routes) {
codec->component.dapm_routes = codec_drv->dapm_routes;
codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
}
if (codec_drv->probe)
codec->component.probe = snd_soc_codec_drv_probe;// codec->driver->probe
if (codec_drv->remove)
codec->component.remove = snd_soc_codec_drv_remove;
if (codec_drv->write)
codec->component.write = snd_soc_codec_drv_write;
if (codec_drv->read)
codec->component.read = snd_soc_codec_drv_read;
codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
......
if (codec_drv->get_regmap)
codec->component.regmap = codec_drv->get_regmap(dev);// remap
for (i = 0; i < num_dai; i++) {//snd_soc_dai_driver Format
fixup_codec_formats(&dai_drv[i].playback);//【3】 fixup_codec_formats(&dai_drv[i].capture); } ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);//【4】 list_for_each_entry(dai, &codec->component.dai_list, list) dai->codec = codec;//【5】 mutex_lock(&client_mutex); snd_soc_component_add_unlocked(&codec->component);//【6】 list_add(&codec->list, &codec_list);//【7】codec_list mutex_unlock(&client_mutex); }
주석은 상【1】sndsoc_component_initialize에 이름과 component 필드 채우기 설정【2】codec->component의 각 필드 설정【3】fixupcodec_formats 설정Format【4】sndsoc_register_dais는 본 Codec의dai에 등록【5】codec의dai를 모두 code->component에 저장합니다.dai_list에서【6】codec에서의component 구성 요소codec->component를component에 추가list 체인 테이블에서【7】codec 실례를 전역 체인 테이블에 연결codeclist에서 Machine 드라이버가 초기화될 때 이 체인 테이블을 옮겨다니며 일치 및 귀속dai링크의 코드c에서 구체적인 분석을 보십시오: 【1】sndsoc_component_initialize
static int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver, struct device *dev)
{
component->name = fmt_single_name(dev, &component->id);
component->dev = dev;
component->driver = driver;
component->probe = component->driver->probe;
component->remove = component->driver->remove;
dapm = &component->dapm;
dapm->dev = dev;
dapm->component = component;
dapm->bias_level = SND_SOC_BIAS_OFF;
dapm->idle_bias_off = true;
if (driver->seq_notifier)
dapm->seq_notifier = snd_soc_component_seq_notifier;
if (driver->stream_event)
dapm->stream_event = snd_soc_component_stream_event;
component->controls = driver->controls;// snd_soc_codec_driver->component_driver
component->num_controls = driver->num_controls;
component->dapm_widgets = driver->dapm_widgets;
component->num_dapm_widgets = driver->num_dapm_widgets;
component->dapm_routes = driver->dapm_routes;
component->num_dapm_routes = driver->num_dapm_routes;
INIT_LIST_HEAD(&component->dai_list);
mutex_init(&component->io_mutex);
}
이 안에 fmtsingle_name 함수는 코드c의 이름을 정하는 것입니다. 이 이름은 매우 중요합니다. Machine 드라이브가 정의한 sndsoc_dai_링크에서 모든 링크의 코드c와dai의 이름을 지정합니다. 일치하는 연결을 진행할 때 이 코드c를 찾을 수 있습니다.이어서 componment 구성 요소를 설정합니다.【4】snd_soc_register_dais 함수
static int snd_soc_register_dais(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv, size_t count,
bool legacy_dai_naming)
{
struct device *dev = component->dev;
struct snd_soc_dai *dai;
unsigned int i;
component->dai_drv = dai_drv;
for (i = 0; i < count; i++) {
dai = soc_add_dai(component, dai_drv + i,
count == 1 && legacy_dai_naming);
if (dai == NULL) {
ret = -ENOMEM;
goto err;
}
}
}
안에 soc 호출이 들어있어요.add_dai 함수, 메모리 분배,component->dai 에 링크리스트에 있습니다.
마지막으로 블로거의 말을 덧붙인다.
마지막으로 Dai link의 codec와 codec dai의 차이점을 말씀드리겠습니다. codec는 오디오 codec가 공유하는 부분을 가리키는데 codec 초기화 함수, 제어 인터페이스, 레지스터 캐시, 컨트롤 목록,dapm 위젯 목록, 오디오 루트 목록, 편치 전압 설정 함수 등 설명 메시지를 포함합니다. 반면에 codec dai는 codec의 오디오 인터페이스 드라이버 설명을 가리키며 각 인터페이스의 설명 정보가 반드시모두 일치하기 때문에 각 오디오 인터페이스는 그 자체의 구동 설명을 가지고 오디오 인터페이스의 초기화 함수, 조작 함수 집합, 능력 설명 등을 포함한다.내가 시작했을 때:codecdai 종속 코드c,dailink에서 codec와 codec 를 동시에 설명할 필요가 없습니다.dai, 코드c 가능할 것 같습니다dai는 부모 장치 코드c에 대응하는 방법을 찾을 수 있습니다.나중에 시스템에 두 개 이상의 코드가 있다면 코드c의 코드가 꼭 다르다고 생각했어요dai에 이름이 겹치면 코드c와 코드c 를 동시에 성명해야 합니다dai가 정확한 오디오 인터페이스를 찾을 수 있습니다.
참조:https://blog.csdn.net/azloong/article/details/17252843참조:https://blog.csdn.net/RadianceBlau/article/details/79432661