Team,
I need some help with below request for a customer that is trying to convert their audio playback application from OSS to ALSA. Please see below details. Can you please assist?
Thanks,
Harini
We are converting the Audio Playback application from OSS to ALSA. In the process I have run into a problem getting the audio device to actually play the data. The application opens the "plughw:0,0" alsa device. It uses the snd_pcm_writei function to send the decoded audio frames to the device. Using printk statements and looking at the code I have determined that the data is written to the internal 8K buffer. I see each write add data to the buffer, however, I can't see any mechanism to take the data out of the buffer and transfer it to the audio device. I am hoping that someone at TI can give me information about how data is copied out of the internal buffer. I have provided the setup code for the audio driver at both kernel level and the application level along with the details provided from the /proc file system. Kernel Setup ------------ static int __init evm_init(void) { int ret = 0; int res_size; struct snd_soc_device *evm_snd_devdata; struct resource *evm_snd_resources; struct evm_snd_platform_data *evm_snd_data; evm_snd_devdata = &dm365_atlas_snd_devdata; evm_snd_resources = dm365_evm_snd_resources; res_size = ARRAY_SIZE(dm365_evm_snd_resources); evm_snd_data = &dm365_evm_snd_data; evm_codec_clock = DM365_EVM_CODEC_CLOCK; evm_snd_device = platform_device_alloc("soc-audio", -1); if (!evm_snd_device) return -ENOMEM; platform_set_drvdata(evm_snd_device, evm_snd_devdata); evm_snd_devdata->dev = &evm_snd_device->dev; evm_snd_device->dev.platform_data = evm_snd_data; ret = platform_device_add_resources(evm_snd_device, evm_snd_resources, res_size); if (ret) { platform_device_put(evm_snd_device); return ret; } ret = platform_device_add(evm_snd_device); if (ret) platform_device_put(evm_snd_device); return ret; } /* --------------------------------------------------------------------*/ evm_snd_devdata = &dm365_atlas_snd_devdata; static struct snd_soc_device dm365_atlas_snd_devdata = { .machine = &dm365_snd_soc_machine_atlas, // see below .platform = &davinci_soc_platform, // see below .codec_dev = &soc_codec_dev_cs4251, // see below .codec_data = &dm365_evm_aic3x_setup, // see below }; static struct snd_soc_machine dm365_snd_soc_machine_atlas = { .name = "DaVinci DM365 ATLAS", .dai_link = &davinci_atlas_dai, // see below .num_links = 1, }; static struct snd_soc_dai_link davinci_atlas_dai = { .name = "CS4251", .stream_name = "CS4251_AIC", .cpu_dai = davinci_i2s_dai, // see below .codec_dai = &cs4251_dai, // see below .init = atlas_cs4251_init, // davinci_evm.c (function) .ops = &evm_ops, // see below }; struct snd_soc_cpu_dai davinci_i2s_dai[] = { { .name = "davinci-i2s", .id = 0, .type = SND_SOC_DAI_I2S, .probe = davinci_i2s_probe, // davinci-i2s.c (function) .remove = davinci_i2s_remove, // davinci-i2s.c (function) .playback = { .channels_min = 1, .channels_max = 2, .rates = DAVINCI_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S32_LE,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = DAVINCI_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S32_LE,}, .ops = { .startup = davinci_i2s_startup, // davinci-i2s.c (function) .trigger = davinci_i2s_trigger, // davinci-i2s-mcbsp.c (function) .hw_params = davinci_i2s_hw_params,}, // davinci-i2s-mcbsp.c (function) .dai_ops = { .set_fmt = davinci_i2s_set_dai_fmt, // davnici-i2s-mcbsp.c (function) }, }, }; struct snd_soc_codec_dai cs4251_dai = { .name = "CS4251", .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = CS4251_RATES, .formats = CS4251_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = 2, .rates = CS4251_RATES, .formats = CS4251_FORMATS,}, .dai_ops = { .digital_mute = NULL, .set_sysclk = cs4251_set_dai_sysclk, // cs4251.c (function) .set_fmt = NULL, } }; static struct snd_soc_ops evm_ops = { .hw_params = evm_hw_params, // davinci-evm.c (function) }; struct snd_soc_platform davinci_soc_platform = { .name = "davinci-audio", .pcm_ops = &davinci_pcm_ops, // see below .pcm_new = davinci_pcm_new, // davinci-pcm.c (function) .pcm_free = davinci_pcm_free, // davinci-pcm.c (function) }; struct snd_pcm_ops davinci_pcm_ops = { .open = davinci_pcm_open, // davinci-pcm.c (function) .close = davinci_pcm_close, // davinci-pcm.c (function) .ioctl = snd_pcm_lib_ioctl, // pcm_lib.c (function) .hw_params = davinci_pcm_hw_params, // davinci-pcm.c (function) .hw_free = davinci_pcm_hw_free, // davinci-pcm.c (function) .prepare = davinci_pcm_prepare, // davinci-pcm.c (function) .trigger = davinci_pcm_trigger, // davinci-pcm.c (function) .pointer = davinci_pcm_pointer, // davinci-pcm.c (function) .mmap = davinci_pcm_mmap, // davinci-pcm.c (function) }; struct snd_soc_codec_device soc_codec_dev_cs4251 = { .probe = cs4251_probe, // cs4251.c (function) .remove = cs4251_remove, // cs4251.c (function) }; static struct aic3x_setup_data dm365_evm_aic3x_setup = { .i2c_address = 0x18, }; /* --------------------------------------------------------------------*/ evm_snd_resources = dm365_evm_snd_resources; res_size = ARRAY_SIZE(dm365_evm_snd_resources); static struct resource dm365_evm_snd_resources[] = { { .start = DM365_MCBSP_BASE, .end = DM365_MCBSP_BASE + SZ_8K - 1, .flags = IORESOURCE_MEM, }, }; /* --------------------------------------------------------------------*/ evm_snd_data = &dm365_evm_snd_data; static struct evm_snd_platform_data dm365_evm_snd_data = { .clk_name = "McBSPCLK", .tx_dma_ch = DM365_DMA_MCBSP_TX, .rx_dma_ch = DM365_DMA_MCBSP_RX, .tx_dma_offset = DAVINCI_MCBSP_DXR_REG, .rx_dma_offset = DAVINCI_MCBSP_DRR_REG, .codec_fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_IF, .eventq_no = EVENTQ_3, }; /* --------------------------------------------------------------------*/ evm_codec_clock = DM365_EVM_CODEC_CLOCK; /* --------------------------------------------------------------------*/ evm_snd_device = platform_device_alloc("soc-audio", -1); /* --------------------------------------------------------------------*/ platform_set_drvdata (evm_snd_device, evm_snd_devdata); evm_snd_devdata->dev = &evm_snd_device->dev; evm_snd_device->dev.platform_data = evm_snd_data; /* --------------------------------------------------------------------*/ ret = platform_device_add_resources (evm_snd_device, evm_snd_resources, res_size); /* --------------------------------------------------------------------*/ ret = platform_device_add (evm_snd_device); Application Setup ----------------- Note that the error checking has been removed from the code below to simplify the details, but in the actual code, each API call is followed by a check of the return call and appropriate handling if it indicates a failure. When the code runs, none of the functions called below fails. typedef struct { snd_pcm_t *pAudioDeviceID; snd_pcm_hw_params_t *pHardwareParams; snd_pcm_sw_params_t *pSoftwareParams; uint32_t nChannelCount; uint32_t nSampleRate; } AudioOutputParams_t; AudioOutputParams_t pOutputParams_ = {0}; snd_pcm_open (&(pOutputParams_->pAudioDeviceID), AUDIO_DEVICE, SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC); snd_pcm_hw_params_malloc (&(pOutputParams_->pHardwareParams)); snd_pcm_sw_params_malloc (&(pOutputParams_->pSoftwareParams)); snd_pcm_sw_params_malloc (&(pOutputParams_->pSoftwareParams)); snd_pcm_hw_params_any (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams); snd_pcm_hw_params_set_access (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams, pOutputParams_->nChannelCount); snd_pcm_hw_params_set_rate_near (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams, &pOutputParams_->nSampleRate, &nDatavalue); snd_pcm_hw_params_set_period_size_near (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams, &nPeriodSize, &nDatavalue); snd_pcm_hw_params (pOutputParams_->pAudioDeviceID, pOutputParams_->pHardwareParams); snd_pcm_sw_params_current (pOutputParams_->pAudioDeviceID, pOutputParams_->pSoftwareParams); snd_pcm_sw_params_get_xfer_align (pOutputParams_->pSoftwareParams, &tTransferAlig); snd_pcm_sw_params_set_start_threshold (pOutputParams_->pAudioDeviceID, pOutputParams_->pSoftwareParams, 0U); snd_pcm_sw_params (pOutputParams_->pAudioDeviceID, pOutputParams_->pSoftwareParams); Details from the /proc file system: # cat /proc/asound/cards 0 [ATLAS ]: cs4251 - DaVinci DM365 ATLAS DaVinci DM365 ATLAS (cs4251) # cat /proc/asound/devices 2: : timer 3: [ 0- 0]: digital audio playback 4: [ 0- 0]: digital audio capture 5: [ 0] : control # cat /proc/asound/card0/id ATLAS # cat /proc/asound/card0/pcm0p/info card: 0 device: 0 subdevice: 0 stream: PLAYBACK id: CS4251_AIC CS4251-I2S-0 name: CS4251-0 subname: subdevice #0 class: 0 subclass: 0 subdevices_count: 1 subdevices_avail: 1 # cat /proc/asound/card0/pcm0p/sub0/hw_params closed # cat /proc/asound/card0/pcm0p/sub0/info card: 0 device: 0 subdevice: 0 stream: PLAYBACK id: CS4251_AIC CS4251-I2S-0 name: CS4251-0 subname: subdevice #0 class: 0 subclass: 0 subdevices_count: 1 subdevices_avail: 1 # cat /proc/asound/card0/pcm0p/sub0/status closed # cat /proc/asound/card0/pcm0p/sub0/sw_params closed --