DM368의 SOC를 세부적으로 본다며, 아래와 같으며, 각 설명 Driver의 구성은 다음과 같다.
- Codec Driver : sound/soc/codecs/tlv320aic3x.c
- Platform Driver: sound/soc/davinci/davinci-i2s.c
- Machine Driver: sound/soc/davinci/davinci-evm.c
http://processors.wiki.ti.com/index.php/Sitara_Linux_Audio_Driver_Overview
2.1 Machine Driver
TI-SOC의 구성 , 아래와 같이 Machine Driver가 기본설정하고 , Platform과 Codec을 연결을 해준다.
정확히 cpu_dai와 codec_dai를 mapping을 하여 동작한다.
$ vi sound/soc/davinci/davinci-evm.c
#define AUDIO_FORMAT ( SND_SOC_DAIFMT_DSP_A | \
SND_SOC_DAIFMT_CBM_CFM | \
SND_SOC_DAIFMT_IB_NF)
static struct snd_soc_dai_link dm365_evm_dai = {
#ifdef CONFIG_SND_DM365_AIC3X_CODEC
.name = "TLV320AIC3X",
.stream_name = "AIC3X",
.cpu_dai_name = "davinci-mcbsp", // CPU에서 사용하는 Audio Driver name , sound/soc/davinci/davinci-i2s.c
.codec_dai_name = "tlv320aic3x-hifi", // CODEC에서 등록한 SOC DAI 이름 sound/soc/codecs/tlv320aic3x.c
.init = evm_aic3x_init,
.codec_name = "tlv320aic3x-codec.1-0018",
.ops = &evm_ops,
#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
.name = "Voice Codec - CQ93VC",
.stream_name = "CQ93",
.cpu_dai_name = "davinci-vcif",
.codec_dai_name = "cq93vc-hifi",
.codec_name = "cq93vc-codec",
#endif
.platform_name = "davinci-pcm-audio", // sound/soc/davinci/davinci-pcm.c ( platform driver )
};
/* davinci dm365 evm audio machine driver */
static struct snd_soc_card dm365_snd_soc_card_evm = { // SOC Card 등록
.name = "DaVinci DM365 EVM",
.dai_link = &dm365_evm_dai,
.num_links = 1,
};
static int __init evm_init(void)
{
.......
} else if (machine_is_davinci_dm365_evm() || machine_is_davinci_dm365_ipnc() || machine_is_davinci_dm368_ipnc()) {
evm_snd_dev_data = &dm365_snd_soc_card_evm;
index = 0;
}
evm_snd_device = platform_device_alloc("soc-audio", index); ///sound/soc/soc-core.c 와 연결
if (!evm_snd_device)
return -ENOMEM;
platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
ret = platform_device_add(evm_snd_device);
if (ret)
platform_device_put(evm_snd_device);
....
}
module_init(evm_init); // module init 하게 되면 아래 순서
soc_probe -> snd_soc_register_card -> snd_soc_instantiate_cards -> snd_soc_instantiate_card(struct snd_soc_card *card)
ALSA device list:
#0: DaVinci DM365 EVM
2.2 Platform Driver
TI에서 제공하는 Audio Driver McBSP or McASP 이며 이는 EDMA와 연결하여 동작한다.
그리고, 아래와 같이 PCM Driver와 연동이 되어 동작이 된다.
PCM Interface Driver로 , Platform Driver 라고 부른다. Machine Driver와 연결이 되어 있으며, PCM의 관련처리를 이곳에서 처리한다.
APP에서 PCM을 OPEN할 경우부터 기본설정되는 부분이 아래에 다 들어가 있다.
$ vi ./sound/soc/davinci/davinci-pcm.c // PCM Driver로 File 정보를 여기서 처리 그리고, EDMA 처리 등을 담당.
static struct snd_pcm_hardware pcm_hardware_playback = { // ALSA의 기본 Playback 관련설정
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
static struct snd_pcm_hardware pcm_hardware_capture = { // ALSA의 기본 Capture 관련설정
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
/*
PCM 기본 동작 Driver , PCM OPEN시, HW_PARAM 정보, TRIGGER, 이는 곳 EDMA와 연결을 시킨다.
*/
static struct snd_pcm_ops davinci_pcm_ops = {
.open = davinci_pcm_open,
.close = davinci_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = davinci_pcm_hw_params, //HW_PARAM 설정 , ALSA APP에 호출될 때 호출 .
.hw_free = davinci_pcm_hw_free,
.prepare = davinci_pcm_prepare, // TI EDMA 준비
.trigger = davinci_pcm_trigger, // TI EDMA Control
.pointer = davinci_pcm_pointer,
.mmap = davinci_pcm_mmap, // DMA 관련 MMAP
};
static struct snd_soc_platform_driver davinci_soc_platform = {
.ops = &davinci_pcm_ops,
.pcm_new = davinci_pcm_new,
.pcm_free = davinci_pcm_free,
};
static int __devinit davinci_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&pdev->dev, &davinci_soc_platform);
}
static struct platform_driver davinci_pcm_driver = {
.driver = {
.name = "davinci-pcm-audio",
.owner = THIS_MODULE,
},
.probe = davinci_soc_platform_probe,
.remove = __devexit_p(davinci_soc_platform_remove),
};
TI사에 제공하는 SOC Driver (McBSP)이며, 이는 PCM Interface Driver와 같이 동작하며,실질적인 SoC의 Audio Driver이다.
McBSP가 HW Codec과 연결되어 실질적인 역할하며, Interface는 I2S이지만, 다른 것으로 변경가능하다 (DSP Mode)
$ vi ./sound/soc/davinci/davinci-i2s.c //MCBSP의 Main Controller 로, 실제 CPU Dai에 연결된다.
static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.startup = davinci_i2s_startup,
.shutdown = davinci_i2s_shutdown,
.prepare = davinci_i2s_prepare,
.trigger = davinci_i2s_trigger,
.hw_params = davinci_i2s_hw_params, // HW_PARAM 설정
.set_fmt = davinci_i2s_set_dai_fmt,
.set_clkdiv = davinci_i2s_dai_set_clkdiv,
};
static struct snd_soc_dai_driver davinci_i2s_dai = { // Capture/Playback I2S Interface의 동작범위 와 Format 정의
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = DAVINCI_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = DAVINCI_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = &davinci_i2s_dai_ops,
};
static int davinci_i2s_probe(struct platform_device *pdev)
{
.....
EDMA 관련 초기화
ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai);
if (ret != 0)
goto err_free_mem;
}
static struct platform_driver davinci_mcbsp_driver = {
.probe = davinci_i2s_probe,
.remove = davinci_i2s_remove,
.driver = {
.name = "davinci-mcbsp",
.owner = THIS_MODULE,
},
};
static int __init davinci_i2s_init(void)
{
return platform_driver_register(&davinci_mcbsp_driver);
}
module_init(davinci_i2s_init);
2.3 HW Codec Driver
Codec Driver는 구조가 거의 비슷하며, I2C Driver로 Codec을 Control 하게되어 각 설정을 변경하고, 등록을 하는 것이다.
이는 soc_dai를 등록하여 동작하게 한다.
Codec를 동작하게 하기위해서는
미리 I2C Device를 등록을 해야 동작이 가능하다.
(Audio-ALSA SOC Codec Driver 참조)
$ vi ./sound/soc/codecs/tlv320aic3x.c // Codec Driver 현재 AIC3100으로 변경 중. 서로 호환이 되지 않음
static struct snd_soc_dai_driver aic3x_dai = {
.name = "tlv320aic3x-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
.ops = &aic3x_dai_ops,
};
static int aic3x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
......
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_aic3x, &aic3x_dai, 1);
}
static struct i2c_driver aic3x_i2c_driver = {
.driver = {
.name = "tlv320aic3x-codec",
.owner = THIS_MODULE,
},
.probe = aic3x_i2c_probe,
.remove = aic3x_i2c_remove,
.id_table = aic3x_i2c_id,
};
static inline void aic3x_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&aic3x_i2c_driver);
if (ret) printk(KERN_ERR "%s: error regsitering i2c driver, %d\n", __func__, ret);
}
static int __init aic3x_modinit(void)
{
int ret = 0;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&aic3x_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register TLV320AIC3x I2C driver: %d\n",
ret);
}
#endif
return ret;
}
module_init(aic3x_modinit);
- AM335x 와 DM81xx Driver Guide
아래의 사이트를 보면, 각 CPU 마다 Driver의 구조 및 FLOW ,구조를 설명을 하고 있다.
http://processors.wiki.ti.com/index.php/AM335x_Audio_Driver's_Guide
http://processors.wiki.ti.com/index.php/DM81xx_AM38xx_Audio_Driver_User_Guide
3. ALSA Card 등록 및 확인
linux kernel log에서 아래와 같이 ALSA device에 추가 되었는지 확인을 하자.
DM368인 경우, TLVAIC3x 코덱을 사용을 하면, 2개의 McBSP Driver의 충돌로 인하여 제대로 등록이 되지 않는다.
ALSA device list:
#0: DaVinci DM365 EVM
ALSA에 등록하여 사용하도록 하자
$ vi sound/soc/davinci/davinci-evm.c
/* davinci dm365 evm audio machine driver */
static struct snd_soc_card dm365_snd_soc_card_evm = {
.name = "DaVinci DM365 EVM",
.dai_link = &dm365_evm_dai,
.num_links = 1,
};
static int __init evm_init(void)
{
.........
} else if (machine_is_davinci_dm365_evm() || machine_is_davinci_dm365_ipnc() || machine_is_davinci_dm368_ipnc()) {
evm_snd_dev_data = &dm365_snd_soc_card_evm;
index = 0;
}
evm_snd_device = platform_device_alloc("soc-audio", index);
if (!evm_snd_device)
return -ENOMEM;
platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
ret = platform_device_add(evm_snd_device);
if (ret)
platform_device_put(evm_snd_device);
........
}
module_init(evm_init);
module_exit(evm_exit);
아래와 같이 davinci-mcbsp platform_device를 등록을 하였지만,이 platfor_driver가 현재 두개가 존재하여 문제가 됨
이중 davinci-i2s.c 선택하여 사용함
- ./sound/soc/davinci/davinci-i2s.c // 이 McBSP는 일반 Codec 사용
- ./arch/arm/mach-davinci/mcbsp.c // 이 McBSP는 Voice Codec을 사용시 사용
$ vi ./arch/arm/mach-davinci/board-dm368-ipnc.c
static struct snd_platform_data dm365_evm_snd_data = {
.asp_chan_q = EVENTQ_3,
};
$ vi ./arch/arm/mach-davinci/dm365.c
static struct resource dm365_asp_resources[] = {
{
.start = DAVINCI_DM365_ASP0_BASE,
.end = DAVINCI_DM365_ASP0_BASE + SZ_8K - 1,
.flags = IORESOURCE_MEM,
},
{
.start = DAVINCI_DMA_ASP0_TX,
.end = DAVINCI_DMA_ASP0_TX,
.flags = IORESOURCE_DMA,
},
{
.start = DAVINCI_DMA_ASP0_RX,
.end = DAVINCI_DMA_ASP0_RX,
.flags = IORESOURCE_DMA,
},
};
static struct platform_device dm365_asp_device = {
.name = "davinci-mcbsp", // 현재 충돌 중. McBSP Driver 가 두 개
.id = -1,
.num_resources = ARRAY_SIZE(dm365_asp_resources),
.resource = dm365_asp_resources,
};
platform_device_register(&dm365_asp_device);