12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208 |
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/platform_device.h>
- #include <linux/init.h>
- #include <linux/errno.h>
- #include <linux/ioctl.h>
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <linux/dma-mapping.h>
- #include <linux/soundcard.h>
- #include <linux/timer.h>
- #include <linux/debugfs.h>
- #include <linux/major.h>
- #include <sound/core.h>
- #include <sound/pcm.h>
- #include <sound/initval.h>
- #include <sound/control.h>
- #include <sound/soc.h>
- #include <sound/pcm_params.h>
- #include <sound/rt5631.h>
- #include <mach/am_regs.h>
- #include <mach/pinmux.h>
- #include <linux/amports/amaudio.h>
- #include <mach/mod_gate.h>
- #include "aml_pcm.h"
- #include "aml_audio_hw.h"
- #define AOUT_EVENT_PREPARE 0x1
- extern int aout_notifier_call_chain(unsigned long val, void *v);
- unsigned int aml_pcm_playback_start_addr = 0;
- unsigned int aml_pcm_capture_start_addr = 0;
- unsigned int aml_pcm_capture_start_phy = 0;
- unsigned int aml_pcm_capture_buf_size = 0;
- unsigned int aml_pcm_playback_off = 0;
- unsigned int aml_pcm_playback_enable = 1;
- /*to keep the pcm status for clockgating*/
- static unsigned clock_gating_status = 0;
- static unsigned clock_gating_playback = 1;
- static unsigned clock_gating_capture = 2;
- static struct rt5631_platform_data *rt5631_snd_pdata = NULL;
- static struct aml_pcm_work_t{
- struct snd_pcm_substream *substream;
- struct work_struct aml_codec_workqueue;
- }aml_pcm_work;
- static int codec_power_switch(struct snd_pcm_substream *substream, unsigned int status);
- EXPORT_SYMBOL(aml_pcm_playback_start_addr);
- EXPORT_SYMBOL(aml_pcm_capture_start_addr);
- EXPORT_SYMBOL(aml_pcm_playback_off);
- EXPORT_SYMBOL(aml_pcm_playback_enable);
- static void aml_codec_power_switch_queue(struct work_struct* work)
- {
- struct aml_pcm_work_t* pwork = container_of(work, struct aml_pcm_work_t, aml_codec_workqueue);
- struct snd_pcm_substream* substream = pwork->substream;
- #ifdef _AML_PCM_DEBUG_
- printk("***Entered %s:%s\n", __FILE__,__func__);
- #endif
- codec_power_switch(substream, clock_gating_status);
- }
- /*--------------------------------------------------------------------------*\
- * Hardware definition
- \*--------------------------------------------------------------------------*/
- /* TODO: These values were taken from the AML platform driver, check
- * them against real values for AML
- */
- static const struct snd_pcm_hardware aml_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED|
- SNDRV_PCM_INFO_BLOCK_TRANSFER|
- SNDRV_PCM_INFO_PAUSE,
-
- .formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE,
- .period_bytes_min = 64,
- .period_bytes_max = 8*1024,
- .periods_min = 2,
- .periods_max = 1024,
- .buffer_bytes_max = 128 * 1024,
-
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .fifo_size = 0,
- };
- static const struct snd_pcm_hardware aml_pcm_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,
- .period_bytes_min = 64,
- .period_bytes_max = 8*1024,
- .periods_min = 2,
- .periods_max = 1024,
- .buffer_bytes_max = 64 * 1024,
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .fifo_size = 0,
- };
- static char snd_pcm_tmp[32*1024];
- /*--------------------------------------------------------------------------*\
- * Data types
- \*--------------------------------------------------------------------------*/
- struct aml_runtime_data {
- struct aml_pcm_dma_params *params;
- dma_addr_t dma_buffer; /* physical address of dma buffer */
- dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
- struct snd_pcm *pcm;
- audio_stream_t s;
- struct timer_list timer; // timeer for playback and capture
- };
- static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
- static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
- .count = ARRAY_SIZE(period_sizes),
- .list = period_sizes,
- .mask = 0
- };
- /*--------------------------------------------------------------------------*\
- * audio clock gating
- \*--------------------------------------------------------------------------*/
- static void aml_audio_clock_gating_disable(void)
- {
- #ifdef _AML_PCM_DEBUG_
- printk("***Entered %s:%s\n", __FILE__,__func__);
- #endif
- /*
- aml_clr_reg32_mask(P_HHI_GCLK_MPEG0, (1<<18));
- aml_clr_reg32_mask(P_HHI_GCLK_MPEG1, (1<<2)
- |(0xba<<6)
- );
- aml_clr_reg32_mask(P_HHI_GCLK_OTHER, (1<<18)
- |(0x6<<14)
- );
- */ aml_clr_reg32_mask( P_HHI_AUD_CLK_CNTL, (1 << 8));
- //printk("P_HHI_GCLK_MPEG0=disable--%#x\n\n", aml_read_reg32(P_HHI_GCLK_MPEG0));
- //printk("P_HHI_GCLK_MPEG1=disable--%#x\n\n", aml_read_reg32(P_HHI_GCLK_MPEG1));
- //printk("P_HHI_GCLK_OTHER=disable--%#x\n\n", aml_read_reg32(P_HHI_GCLK_OTHER));
- }
- static void aml_audio_clock_gating_enable(void)
- {
- #ifdef _AML_PCM_DEBUG_
- printk("***Entered %s:%s\n", __FILE__,__func__);
- #endif
- /* aml_set_reg32_mask(P_HHI_GCLK_MPEG0, (1<<18));
- aml_set_reg32_mask(P_HHI_GCLK_MPEG1, (1<<2)
- |(0xba<<6)
- );
- aml_set_reg32_mask(P_HHI_GCLK_OTHER, (1<<18)
- |(0x6<<14)
- );
- */ aml_set_reg32_mask( P_HHI_AUD_CLK_CNTL, (1 << 8));
- //printk("P_HHI_GCLK_MPEG0=enable--%#x\n\n", aml_read_reg32(P_HHI_GCLK_MPEG0));
- //printk("P_HHI_GCLK_MPEG1=enable--%#x\n\n", aml_read_reg32(P_HHI_GCLK_MPEG1));
- //printk("P_HHI_GCLK_OTHER=enable--%#x\n\n", aml_read_reg32(P_HHI_GCLK_OTHER));
- }
- static void aml_clock_gating(unsigned int status)
- {
- //printk("-----status=%d\n\n",status);
- if(status){
- aml_audio_clock_gating_enable();
- }
- else{
- aml_audio_clock_gating_disable();
- }
- }
- /*--------------------------------------------------------------------------*\
- * audio codec power management
- \*--------------------------------------------------------------------------*/
- static int codec_power_switch(struct snd_pcm_substream *substream, unsigned int status)
- {
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- #ifdef _AML_PCM_DEBUG_
- printk("***Entered %s:%s\n", __FILE__,__func__);
- #endif
- if(status && codec_dai->driver->ops->startup)
- codec_dai->driver->ops->startup(substream, codec_dai);
-
- if(!status && codec_dai->driver->ops->shutdown)
- codec_dai->driver->ops->shutdown(substream, codec_dai);
- return 0;
- }
- /*--------------------------------------------------------------------------*\
- * Helper functions
- \*--------------------------------------------------------------------------*/
- static int aml_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
- int stream)
- {
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
-
- size_t size = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- size = aml_pcm_hardware.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- /* one size for i2s output, another for 958, and 128 for alignment */
- buf->area = dma_alloc_coherent(pcm->card->dev, size*2+128,
- &buf->addr, GFP_KERNEL);
- printk("aml-pcm %d:"
- "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", stream,
- (void *) buf->area,
- (void *) buf->addr,
- size);
- aml_pcm_playback_start_addr = (unsigned int)buf->area;
- }else{
- size = aml_pcm_capture.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(pcm->card->dev, size*2,
- &buf->addr, GFP_KERNEL);
- printk("aml-pcm %d:"
- "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", stream,
- (void *) buf->area,
- (void *) buf->addr,
- size);
- aml_pcm_capture_start_addr = (unsigned int)buf->area;
- aml_pcm_capture_start_phy = buf->addr;
- aml_pcm_capture_buf_size = size;
- }
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
- }
- /*--------------------------------------------------------------------------*\
- * ISR
- \*--------------------------------------------------------------------------*/
- /*--------------------------------------------------------------------------*\
- * PCM operations
- \*--------------------------------------------------------------------------*/
- static int aml_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct aml_runtime_data *prtd = runtime->private_data;
- // struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- audio_stream_t *s = &prtd->s;
-
- /* this may get called several times by oss emulation
- * with different params */
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
- s->I2S_addr = runtime->dma_addr;
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- /* s->last_ptr must initialized as dma buffer's start addr */
- s->last_ptr = runtime->dma_addr;
- }
-
- return 0;
- }
- static int aml_pcm_hw_free(struct snd_pcm_substream *substream)
- {
- struct aml_runtime_data *prtd = substream->runtime->private_data;
- struct aml_pcm_dma_params *params = prtd->params;
- if (params != NULL) {
-
- }
- return 0;
- }
- static int aml_pcm_prepare(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct aml_runtime_data *prtd = runtime->private_data;
- audio_stream_t *s = &prtd->s;
- int iec958 = 0;
-
- if(prtd == 0)
- return 0;
-
- switch(runtime->rate){
- case 192000:
- s->sample_rate = AUDIO_CLK_FREQ_192;
- break;
- case 176400:
- s->sample_rate = AUDIO_CLK_FREQ_1764;
- break;
- case 96000:
- s->sample_rate = AUDIO_CLK_FREQ_96;
- break;
- case 88200:
- s->sample_rate = AUDIO_CLK_FREQ_882;
- break;
- case 48000:
- s->sample_rate = AUDIO_CLK_FREQ_48;
- iec958 = 2;
- break;
- case 44100:
- s->sample_rate = AUDIO_CLK_FREQ_441;
- iec958 = 0;
- break;
- case 32000:
- s->sample_rate = AUDIO_CLK_FREQ_32;
- iec958 = 3;
- break;
- case 8000:
- s->sample_rate = AUDIO_CLK_FREQ_8;
- break;
- case 11025:
- s->sample_rate = AUDIO_CLK_FREQ_11;
- break;
- case 16000:
- s->sample_rate = AUDIO_CLK_FREQ_16;
- break;
- case 22050:
- s->sample_rate = AUDIO_CLK_FREQ_22;
- break;
- case 12000:
- s->sample_rate = AUDIO_CLK_FREQ_12;
- break;
- case 24000:
- s->sample_rate = AUDIO_CLK_FREQ_22;
- break;
- default:
- s->sample_rate = AUDIO_CLK_FREQ_441;
- break;
- };
- audio_set_clk(s->sample_rate, AUDIO_CLK_256FS);
- audio_util_set_dac_format(AUDIO_ALGOUT_DAC_FORMAT_DSP);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- //printk("aml_pcm_prepare SNDRV_PCM_STREAM_PLAYBACK: dma_addr=%x, dma_bytes=%x\n", runtime->dma_addr, runtime->dma_bytes);
- _aiu_958_channel_status_t set;
- _aiu_958_raw_setting_t raw_set;
- memset((void*)(&raw_set), 0, sizeof(raw_set));
- memset((void*)(&set), 0, sizeof(set));
- raw_set.chan_stat = &set;
- audio_set_aiubuf(runtime->dma_addr, runtime->dma_bytes);
-
- switch(runtime->format){
- case SNDRV_PCM_FORMAT_S32_LE:
- I2S_MODE = AIU_I2S_MODE_PCM32;
- IEC958_MODE = AIU_958_MODE_PCM32;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- I2S_MODE = AIU_I2S_MODE_PCM24;
- IEC958_MODE = AIU_958_MODE_PCM24;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- I2S_MODE = AIU_I2S_MODE_PCM16;
- IEC958_MODE = AIU_958_MODE_PCM16;
- break;
- }
- audio_set_i2s_mode(I2S_MODE);
- if(IEC958_MODE == AIU_958_MODE_PCM16 || IEC958_MODE == AIU_958_MODE_PCM24 ||
- IEC958_MODE == AIU_958_MODE_PCM32){
- set.chstat0_l = 0x0100;
- set.chstat0_r = 0x0100;
- audio_set_958outbuf(runtime->dma_addr, runtime->dma_bytes);
- }else{
- set.chstat0_l = 0x1902;
- set.chstat0_r = 0x1902;
- audio_set_958outbuf((runtime->dma_addr+runtime->dma_bytes+127)&(~127), runtime->dma_bytes);
- }
- audio_set_958_mode(IEC958_MODE, &raw_set);
- memset((void*)runtime->dma_area,0,runtime->dma_bytes * 2 + 128);
- }
- else{
- //printk("aml_pcm_prepare SNDRV_PCM_STREAM_CAPTURE: dma_addr=%x, dma_bytes=%x\n", runtime->dma_addr, runtime->dma_bytes);
- audio_in_i2s_set_buf(runtime->dma_addr, runtime->dma_bytes*2);
- memset((void*)runtime->dma_area,0,runtime->dma_bytes*2);
- {
- int * ppp = (int*)(runtime->dma_area+runtime->dma_bytes*2-8);
- ppp[0] = 0x78787878;
- ppp[1] = 0x78787878;
- }
- }
- aout_notifier_call_chain(AOUT_EVENT_PREPARE, substream);
- #if 0
- printk("Audio Parameters:\n");
- printk("\tsample rate: %d\n", runtime->rate);
- printk("\tchannel: %d\n", runtime->channels);
- printk("\tsample bits: %d\n", runtime->sample_bits);
- printk("\tformat: %s\n", snd_pcm_format_name(runtime->format));
- printk("\tperiod size: %ld\n", runtime->period_size);
- printk("\tperiods: %d\n", runtime->periods);
- #endif
-
- return 0;
- }
- static int aml_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
- {
- struct snd_pcm_runtime *rtd = substream->runtime;
- struct aml_runtime_data *prtd = rtd->private_data;
- audio_stream_t *s = &prtd->s;
- int ret = 0;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- #ifdef CONFIG_ARCH_MESON6
- switch_mod_gate_by_type(MOD_AUDIO, 1);
- #endif
-
- del_timer_sync(&prtd->timer);
- spin_lock(&s->lock);
- prtd->timer.expires = jiffies + 1;
- del_timer(&prtd->timer);
- add_timer(&prtd->timer);
-
- // TODO
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- // printk("aml_pcm_trigger: playback start\n");
- clock_gating_status |= clock_gating_playback;
- //aml_clock_gating(clock_gating_status);
- //codec_power_switch(substream, clock_gating_status);
- audio_enable_ouput(1);
- }else{
- // printk("aml_pcm_trigger: capture start\n");
- clock_gating_status |= clock_gating_capture;
- //aml_clock_gating(clock_gating_status);
- //codec_power_switch(substream, clock_gating_status);
- audio_in_i2s_enable(1);
- {
- int * ppp = (int*)(rtd->dma_area+rtd->dma_bytes*2-8);
- ppp[0] = 0x78787878;
- ppp[1] = 0x78787878;
- }
- }
-
- s->active = 1;
- spin_unlock(&s->lock);
- break; /* SNDRV_PCM_TRIGGER_START */
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_STOP:
- // TODO
- spin_lock(&s->lock);
- s->active = 0;
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- // printk("aml_pcm_trigger: playback stop\n");
- audio_enable_ouput(0);
- clock_gating_status &= clock_gating_capture;
- //aml_clock_gating(clock_gating_status);
- //codec_power_switch(substream, clock_gating_status);
- }else{
- // printk("aml_pcm_trigger: capture stop\n");
- audio_in_i2s_enable(0);
- }
- #ifdef CONFIG_ARCH_MESON6
- switch_mod_gate_by_type(MOD_AUDIO, 0);
- #endif
- spin_unlock(&s->lock);
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- // TODO
- spin_lock(&s->lock);
- #ifdef CONFIG_ARCH_MESON6
- switch_mod_gate_by_type(MOD_AUDIO, 1);
- #endif
- s->active = 1;
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- // printk("aml_pcm_trigger: playback resume\n");
- audio_enable_ouput(1);
- clock_gating_status |= clock_gating_playback;
- //aml_clock_gating(clock_gating_status);
- //codec_power_switch(substream, clock_gating_status);
- }else{
- // printk("aml_pcm_trigger: capture resume\n");
- audio_in_i2s_enable(1);
- clock_gating_status |= clock_gating_capture;
- //aml_clock_gating(clock_gating_status);
- //codec_power_switch(substream, clock_gating_status);
- {
- int * ppp = (int*)(rtd->dma_area+rtd->dma_bytes*2-8);
- ppp[0] = 0x78787878;
- ppp[1] = 0x78787878;
- }
- }
- spin_unlock(&s->lock);
- break;
- default:
- ret = -EINVAL;
- }
- aml_pcm_work.substream = substream;
- schedule_work(&aml_pcm_work.aml_codec_workqueue);
- return ret;
- }
- static snd_pcm_uframes_t aml_pcm_pointer(
- struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct aml_runtime_data *prtd = runtime->private_data;
- audio_stream_t *s = &prtd->s;
-
- unsigned int addr, ptr;
-
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- ptr = read_i2s_rd_ptr();
- addr = ptr - s->I2S_addr;
- return bytes_to_frames(runtime, addr);
- }else{
- ptr = audio_in_i2s_wr_ptr();
- addr = ptr - s->I2S_addr;
- return bytes_to_frames(runtime, addr)/2;
- }
-
- return 0;
- }
- static void aml_pcm_timer_callback(unsigned long data)
- {
- struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct aml_runtime_data *prtd = runtime->private_data;
- audio_stream_t *s = &prtd->s;
- unsigned int last_ptr, size;
- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- if(s->active == 1){
- spin_lock(&s->lock);
- last_ptr = read_i2s_rd_ptr();
- if (last_ptr < s->last_ptr) {
- size = runtime->dma_bytes + last_ptr - (s->last_ptr);
- } else {
- size = last_ptr - (s->last_ptr);
- }
- s->last_ptr = last_ptr;
- s->size += bytes_to_frames(substream->runtime, size);
- if (s->size >= runtime->period_size) {
- s->size %= runtime->period_size;
- spin_unlock(&s->lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&s->lock);
- }
- mod_timer(&prtd->timer, jiffies + 1);
- spin_unlock(&s->lock);
- }else{
- mod_timer(&prtd->timer, jiffies + 1);
- }
- }else{
- if(s->active == 1){
- spin_lock(&s->lock);
- last_ptr = (audio_in_i2s_wr_ptr() - s->I2S_addr) / 2;
- if (last_ptr < s->last_ptr) {
- size = runtime->dma_bytes + last_ptr - (s->last_ptr);
- } else {
- size = last_ptr - (s->last_ptr);
- }
- s->last_ptr = last_ptr;
- s->size += bytes_to_frames(substream->runtime, size);
- if (s->size >= runtime->period_size) {
- s->size %= runtime->period_size;
- spin_unlock(&s->lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&s->lock);
- }
- mod_timer(&prtd->timer, jiffies + 1);
- spin_unlock(&s->lock);
- }else{
- mod_timer(&prtd->timer, jiffies + 1);
- }
- }
- }
- static int aml_pcm_open(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct aml_runtime_data *prtd;
- int ret = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- snd_soc_set_runtime_hwparams(substream, &aml_pcm_hardware);
- }else{
- snd_soc_set_runtime_hwparams(substream, &aml_pcm_capture);
- }
-
- /* ensure that peroid size is a multiple of 32bytes */
- ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_sizes);
- if (ret < 0)
- {
- printk("set period bytes constraint error\n");
- goto out;
- }
- /* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- {
- printk("set period error\n");
- goto out;
- }
- prtd = kzalloc(sizeof(struct aml_runtime_data), GFP_KERNEL);
- if (prtd == NULL) {
- printk("alloc aml_runtime_data error\n");
- ret = -ENOMEM;
- goto out;
- }
-
- prtd->pcm = substream->pcm;
- prtd->timer.function = &aml_pcm_timer_callback;
- prtd->timer.data = (unsigned long)substream;
- init_timer(&prtd->timer);
-
- runtime->private_data = prtd;
-
- spin_lock_init(&prtd->s.lock);
- out:
- return ret;
- }
- static int aml_pcm_close(struct snd_pcm_substream *substream)
- {
- struct aml_runtime_data *prtd = substream->runtime->private_data;
-
- del_timer_sync(&prtd->timer);
-
- kfree(prtd);
-
- return 0;
- }
- static int aml_pcm_copy_playback(struct snd_pcm_runtime *runtime, int channel,
- snd_pcm_uframes_t pos,
- void __user *buf, snd_pcm_uframes_t count)
- {
- int res = 0;
- int n;
- int i = 0, j = 0;
- int align = runtime->channels * 32 / runtime->byte_align;
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
- n = frames_to_bytes(runtime, count);
- if(aml_pcm_playback_enable == 0)
- return res;
- if(access_ok(VERIFY_READ, buf, frames_to_bytes(runtime, count))){
- if(runtime->format == SNDRV_PCM_FORMAT_S16_LE && I2S_MODE == AIU_I2S_MODE_PCM16){
- int16_t * tfrom, *to, *left, *right;
- tfrom = (int16_t*)buf;
- to = (int16_t*)hwbuf;
- left = to;
- right = to + 16;
- if (pos % align) {
- printk("audio data unligned: pos=%d, n=%d, align=%d\n", (int)pos, n, align);
- }
- for (j = 0; j < n; j += 64) {
- for (i = 0; i < 16; i++) {
- *left++ = (*tfrom++) ;
- *right++ = (*tfrom++);
- }
- left += 16;
- right += 16;
- }
- }else if(runtime->format == SNDRV_PCM_FORMAT_S24_LE && I2S_MODE == AIU_I2S_MODE_PCM24){
- int32_t *tfrom, *to, *left, *right;
- tfrom = (int32_t*)buf;
- to = (int32_t*) hwbuf;
- left = to;
- right = to + 8;
- if(pos % align){
- printk("audio data unaligned: pos=%d, n=%d, align=%d\n", (int)pos, n, align);
- }
- for(j=0; j< n; j+= 64){
- for(i=0; i<8; i++){
- *left++ = (*tfrom ++);
- *right++ = (*tfrom ++);
- }
- left += 8;
- right += 8;
- }
- }else if(runtime->format == SNDRV_PCM_FORMAT_S32_LE && I2S_MODE == AIU_I2S_MODE_PCM32){
- int32_t *tfrom, *to, *left, *right;
- tfrom = (int32_t*)buf;
- to = (int32_t*) hwbuf;
-
- left = to;
- right = to + 8;
-
- if(pos % align){
- printk("audio data unaligned: pos=%d, n=%d, align=%d\n", (int)pos, n, align);
- }
- for(j=0; j< n; j+= 64){
- for(i=0; i<8; i++){
- *left++ = (*tfrom ++)>>8;
- *right++ = (*tfrom ++)>>8;
- }
- left += 8;
- right += 8;
- }
- }
- }else{
- res = -EFAULT;
- }
-
- return res;
- }
-
- static int aml_pcm_copy_capture(struct snd_pcm_runtime *runtime, int channel,
- snd_pcm_uframes_t pos,
- void __user *buf, snd_pcm_uframes_t count)
- {
- unsigned int *tfrom, *left, *right;
- unsigned short *to;
- int res = 0;
- int n;
- int i = 0, j = 0;
- unsigned int t1, t2;
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos)*2;
-
- to = (unsigned short *)snd_pcm_tmp;//buf;
- tfrom = (unsigned int *)hwbuf; // 32bit buffer
- n = frames_to_bytes(runtime, count);
- if(n > 32*1024){
- printk("Too many datas to read\n");
- return -EINVAL;
- }
-
- if(access_ok(VERIFY_WRITE, buf, frames_to_bytes(runtime, count))){
- left = tfrom;
- right = tfrom + 8;
- if (pos % 8) {
- printk("audio data unligned\n");
- }
- if((n*2)%64){
- printk("audio data unaligned 64 bytes\n");
- }
- for (j = 0; j < n*2 ; j += 64) {
- for (i = 0; i < 8; i++) {
- t1 = (*left++);
- t2 = (*right++);
- //printk("%08x,%08x,", t1, t2);
- *to++ = (unsigned short)((t1>>8)&0xffff);
- *to++ = (unsigned short)((t1>>8)&0xffff);//copy left channel to right
- //*to++ = (unsigned short)((t2>>8)&0xffff);
- }
- //printk("\n");
- left += 8;
- right += 8;
- }
- }
- res = copy_to_user(buf, snd_pcm_tmp,n);
- return res;
- }
- static int aml_pcm_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos,
- void __user *buf, snd_pcm_uframes_t count)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
- ret = aml_pcm_copy_playback(runtime, channel,pos, buf, count);
- }else{
- ret = aml_pcm_copy_capture(runtime, channel,pos, buf, count);
- }
- return ret;
- }
- int aml_pcm_silence(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
- {
- char* ppos;
- int n;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- n = frames_to_bytes(runtime, count);
- ppos = runtime->dma_area + frames_to_bytes(runtime, pos);
- memset(ppos, 0, n);
- return 0;
- }
-
- static struct snd_pcm_ops aml_pcm_ops = {
- .open = aml_pcm_open,
- .close = aml_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = aml_pcm_hw_params,
- .hw_free = aml_pcm_hw_free,
- .prepare = aml_pcm_prepare,
- .trigger = aml_pcm_trigger,
- .pointer = aml_pcm_pointer,
- .copy = aml_pcm_copy,
- .silence = aml_pcm_silence,
- };
- /*--------------------------------------------------------------------------*\
- * ASoC platform driver
- \*--------------------------------------------------------------------------*/
- static u64 aml_pcm_dmamask = 0xffffffff;
- static int aml_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
- {
- int ret = 0;
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &aml_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
- if (dai->driver->playback.channels_min) {
- ret = aml_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
- if (dai->driver->capture.channels_min) {
- pr_debug("aml-pcm:"
- "Allocating PCM capture DMA buffer\n");
- ret = aml_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
-
- out:
- return ret;
- }
- static void aml_pcm_free_dma_buffers(struct snd_pcm *pcm)
- {
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_coherent(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
- aml_pcm_playback_start_addr = 0;
- aml_pcm_capture_start_addr = 0;
- }
- #ifdef CONFIG_PM
- static int aml_pcm_suspend(struct snd_soc_dai *dai)
- {
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct aml_runtime_data *prtd;
- struct aml_pcm_dma_params *params;
- if (!runtime)
- return 0;
- prtd = runtime->private_data;
- params = prtd->params;
- /* disable the PDC and save the PDC registers */
- // TODO
- printk("aml pcm suspend\n");
- return 0;
- }
- static int aml_pcm_resume(struct snd_soc_dai *dai)
- {
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct aml_runtime_data *prtd;
- struct aml_pcm_dma_params *params;
- if (!runtime)
- return 0;
- prtd = runtime->private_data;
- params = prtd->params;
- /* restore the PDC registers and enable the PDC */
- // TODO
- printk("aml pcm resume\n");
- return 0;
- }
- #else
- #define aml_pcm_suspend NULL
- #define aml_pcm_resume NULL
- #endif
- #ifdef CONFIG_DEBUG_FS
- static struct dentry *debugfs_root;
- static struct dentry *debugfs_regs;
- static struct dentry *debugfs_mems;
- static int regs_open_file(struct inode *inode, struct file *file)
- {
- return 0;
- }
- /**
- * cat regs
- */
- static ssize_t regs_read_file(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- ssize_t ret;
- char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = sprintf(buf, "Usage: \n"
- " echo base reg val >regs\t(set the register)\n"
- " echo base reg >regs\t(show the register)\n"
- " base -> c(cbus), x(aix), p(apb), h(ahb) \n"
- );
-
- if (ret >= 0)
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
-
- return ret;
- }
- static int read_regs(char base, int reg)
- {
- int val = 0;
- switch(base){
- case 'c':
- val = READ_CBUS_REG(reg);
- break;
- case 'x':
- val = READ_AXI_REG(reg);
- break;
- case 'p':
- val = READ_APB_REG(reg);
- break;
- case 'h':
- val = READ_AHB_REG(reg);
- break;
- default:
- break;
- };
- printk("\tReg %x = %x\n", reg, val);
- return val;
- }
- static void write_regs(char base, int reg, int val)
- {
- switch(base){
- case 'c':
- WRITE_CBUS_REG(reg, val);
- break;
- case 'x':
- WRITE_AXI_REG(reg, val);
- break;
- case 'p':
- WRITE_APB_REG(reg, val);
- break;
- case 'h':
- WRITE_AHB_REG(reg, val);
- break;
- default:
- break;
- };
- printk("Write reg:%x = %x\n", reg, val);
- }
- static ssize_t regs_write_file(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
- {
- char buf[32];
- int buf_size = 0;
- char *start = buf;
- unsigned long reg, value;
- char base;
-
- buf_size = min(count, (sizeof(buf)-1));
-
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
- while (*start == ' ')
- start++;
-
- base = *start;
- start ++;
- if(!(base =='c' || base == 'x' || base == 'p' || base == 'h')){
- return -EINVAL;
- }
-
- while (*start == ' ')
- start++;
-
- reg = simple_strtoul(start, &start, 16);
-
- while (*start == ' ')
- start++;
-
- if (strict_strtoul(start, 16, &value))
- {
- read_regs(base, reg);
- return -EINVAL;
- }
-
- write_regs(base, reg, value);
-
- return buf_size;
- }
- static const struct file_operations regs_fops = {
- .open = regs_open_file,
- .read = regs_read_file,
- .write = regs_write_file,
- };
- static int mems_open_file(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static ssize_t mems_read_file(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- ssize_t ret;
- char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = sprintf(buf, "Usage: \n"
- " echo vmem >mems\t(read 64 bytes from vmem)\n"
- " echo vmem val >mems (write int value to vmem\n"
- );
-
- if (ret >= 0)
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
- kfree(buf);
-
- return ret;
- }
- static ssize_t mems_write_file(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
- {
- char buf[256];
- int buf_size = 0;
- char *start = buf;
- unsigned long mem, value;
- int i=0;
- unsigned* addr = 0;
-
- buf_size = min(count, (sizeof(buf)-1));
-
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = 0;
-
- while (*start == ' ')
- start++;
-
- mem = simple_strtoul(start, &start, 16);
-
- while (*start == ' ')
- start++;
-
- if (strict_strtoul(start, 16, &value))
- {
- addr = (unsigned*)mem;
- printk("%p: ", addr);
- for(i = 0; i< 8; i++){
- printk("%08x, ", addr[i]);
- }
- printk("\n");
- return -EINVAL;
- }
- addr = (unsigned*)mem;
- printk("%p: %08x\n", addr, *addr);
- *addr = value;
- printk("%p: %08x^\n", addr, *addr);
-
- return buf_size;
- }
- static const struct file_operations mems_fops={
- .open = mems_open_file,
- .read = mems_read_file,
- .write = mems_write_file,
- };
- static void aml_pcm_init_debugfs(void)
- {
- debugfs_root = debugfs_create_dir("aml",NULL);
- if (IS_ERR(debugfs_root) || !debugfs_root) {
- printk("aml: Failed to create debugfs directory\n");
- debugfs_root = NULL;
- }
-
- debugfs_regs = debugfs_create_file("regs", 0644, debugfs_root, NULL, ®s_fops);
- if(!debugfs_regs){
- printk("aml: Failed to create debugfs file\n");
- }
-
- debugfs_mems = debugfs_create_file("mems", 0644, debugfs_root, NULL, &mems_fops);
- if(!debugfs_mems){
- printk("aml: Failed to create debugfs file\n");
- }
- }
- static void aml_pcm_cleanup_debugfs(void)
- {
- debugfs_remove_recursive(debugfs_root);
- }
- #else
- static void aml_pcm_init_debugfs(void)
- {
- }
- static void aml_pcm_cleanup_debugfs(void)
- {
- }
- #endif
- struct snd_soc_platform_driver aml_soc_platform = {
- .ops = &aml_pcm_ops,
- .pcm_new = aml_pcm_new,
- .pcm_free = aml_pcm_free_dma_buffers,
- .suspend = aml_pcm_suspend,
- .resume = aml_pcm_resume,
- };
- EXPORT_SYMBOL_GPL(aml_soc_platform);
- static int __devinit aml_soc_platform_probe(struct platform_device *pdev)
- {
- INIT_WORK(&aml_pcm_work.aml_codec_workqueue, aml_codec_power_switch_queue);
- return snd_soc_register_platform(&pdev->dev, &aml_soc_platform);
- }
- static int __devexit aml_soc_platform_remove(struct platform_device *pdev)
- {
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
- }
- static struct platform_driver aml_pcm_driver = {
- .driver = {
- .name = "aml-audio",
- .owner = THIS_MODULE,
- },
- .probe = aml_soc_platform_probe,
- .remove = __devexit_p(aml_soc_platform_remove),
- };
- static int __init aml_alsa_audio_init(void)
- {
- aml_pcm_init_debugfs();
- return platform_driver_register(&aml_pcm_driver);
- }
- static void __exit aml_alsa_audio_exit(void)
- {
- aml_pcm_cleanup_debugfs();
- platform_driver_unregister(&aml_pcm_driver);
- }
- module_init(aml_alsa_audio_init);
- module_exit(aml_alsa_audio_exit);
- MODULE_AUTHOR("AMLogic, Inc.");
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("AML audio driver for ALSA");
|