From: Grant Likely <hidden> Date: 2008-07-12 08:39:32
From: Grant Likely <redacted>
Simple utility layer for creating ASoC machine instances based on data
in the OpenFirmware device tree. OF aware platform drivers and codec
drivers register themselves with this framework and the framework
automatically instantiates a machine driver.
This is most likely temporary glue code to work around limitations in
the ASoC v1 framework. I expect ASoC v2 won't need this.
---
include/sound/soc-of.h | 21 ++++++
sound/soc/Kconfig | 3 +
sound/soc/Makefile | 1
sound/soc/soc-of.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 196 insertions(+), 0 deletions(-)
@@ -23,6 +23,9 @@ config SND_SOCThisASoCaudiosupportcanalsobebuiltasamodule.Ifso,themodulewillbecalledsnd-soc-core.+configSND_SOC_OF+tristate+# All the supported Soc'ssource"sound/soc/at91/Kconfig"source"sound/soc/pxa/Kconfig"
@@ -0,0 +1,171 @@+/*+*OFhelpersforALSASoCLayer+*+*Copyright(C)2008,SecretLabTechnologiesLtd.+*/++#include<linux/module.h>+#include<linux/moduleparam.h>+#include<linux/init.h>+#include<linux/delay.h>+#include<linux/pm.h>+#include<linux/bitops.h>+#include<linux/platform_device.h>+#include<linux/of.h>+#include<sound/core.h>+#include<sound/pcm.h>+#include<sound/pcm_params.h>+#include<sound/soc.h>+#include<sound/soc-of.h>+#include<sound/initval.h>++MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");+MODULE_LICENSE("GPL");+MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");++staticDEFINE_MUTEX(of_snd_soc_mutex);+staticLIST_HEAD(of_snd_soc_device_list);+staticintof_snd_soc_next_index;++structof_snd_soc_device{+intid;+structlist_headlist;+structsnd_soc_devicedevice;+structsnd_soc_machinemachine;+structsnd_soc_dai_linkdai_link;+structplatform_device*pdev;+structdevice_node*platform_node;+structdevice_node*codec_node;+};++staticstructsnd_soc_opsof_snd_soc_ops={+};++staticstructof_snd_soc_device*+of_snd_soc_get_device(structdevice_node*codec_node)+{+structof_snd_soc_device*of_soc;++list_for_each_entry(of_soc,&of_snd_soc_device_list,list){+if(of_soc->codec_node==codec_node)+returnof_soc;+}++of_soc=kzalloc(sizeof(structof_snd_soc_device),GFP_KERNEL);+if(!of_soc)+returnNULL;++/* Initialize the structure and add it to the global list */+of_soc->codec_node=codec_node;+of_soc->id=of_snd_soc_next_index++;+of_soc->machine.dai_link=&of_soc->dai_link;+of_soc->machine.num_links=1;+of_soc->device.machine=&of_soc->machine;+of_soc->dai_link.ops=&of_snd_soc_ops;+list_add(&of_soc->list,&of_snd_soc_device_list);++returnof_soc;+}++staticvoidof_snd_soc_register_device(structof_snd_soc_device*of_soc)+{+structplatform_device*pdev;+intrc;++/* Only register the device if both the codec and platform have+*beenregistered*/+if((!of_soc->device.codec_data)||(!of_soc->platform_node))+return;++pr_info("platform<-->codec match achieved; registering machine\n");++pdev=platform_device_alloc("soc-audio",of_soc->id);+if(!pdev){+pr_err("of_soc: platform_device_alloc() failed\n");+return;+}++pdev->dev.platform_data=of_soc;+platform_set_drvdata(pdev,&of_soc->device);+of_soc->device.dev=&pdev->dev;++/* The ASoC device is complete; register it */+rc=platform_device_add(pdev);+if(rc){+pr_err("of_soc: platform_device_add() failed\n");+return;+}++}++intof_snd_soc_register_codec(structsnd_soc_codec_device*codec_dev,+void*codec_data,structsnd_soc_codec_dai*dai,+structdevice_node*node)+{+structof_snd_soc_device*of_soc;+intrc=0;++pr_info("registering ASoC codec driver: %s\n",node->full_name);++mutex_lock(&of_snd_soc_mutex);+of_soc=of_snd_soc_get_device(node);+if(!of_soc){+rc=-ENOMEM;+gotoout;+}++/* Store the codec data */+of_soc->device.codec_data=codec_data;+of_soc->device.codec_dev=codec_dev;+of_soc->dai_link.name=node->name;+of_soc->dai_link.stream_name=node->name;+of_soc->dai_link.codec_dai=dai;++/* Now try to register the SoC device */+of_snd_soc_register_device(of_soc);++out:+mutex_unlock(&of_snd_soc_mutex);+returnrc;+}+EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);++intof_snd_soc_register_platform(structsnd_soc_platform*platform,+structdevice_node*node,+structsnd_soc_cpu_dai*cpu_dai)+{+structof_snd_soc_device*of_soc;+structdevice_node*codec_node;+constphandle*handle;+intlen,rc=0;++pr_info("registering ASoC platform driver: %s\n",node->full_name);++handle=of_get_property(node,"codec-handle",&len);+if(!handle||len<sizeof(handle))+return-ENODEV;+codec_node=of_find_node_by_phandle(*handle);+if(!codec_node)+return-ENODEV;+pr_info("looking for codec: %s\n",codec_node->full_name);++mutex_lock(&of_snd_soc_mutex);+of_soc=of_snd_soc_get_device(codec_node);+if(!of_soc){+rc=-ENOMEM;+gotoout;+}++of_soc->platform_node=node;+of_soc->dai_link.cpu_dai=cpu_dai;+of_soc->device.platform=platform;+of_soc->machine.name=of_soc->dai_link.cpu_dai->name;++/* Now try to register the SoC device */+of_snd_soc_register_device(of_soc);++out:+mutex_unlock(&of_snd_soc_mutex);+returnrc;+}+EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
From: Grant Likely <hidden> Date: 2008-07-12 08:39:37
From: Grant Likely <redacted>
This is an I2S bus driver for the MPC5200 PSC device. It is probably
will not be merged as-is because it uses v1 of the ASoC API, but I want
to get it out there for comments.
---
include/asm-powerpc/mpc52xx_psc.h | 32 +
sound/soc/fsl/Kconfig | 7
sound/soc/fsl/Makefile | 2
sound/soc/fsl/mpc5200_psc_i2s.c | 896 +++++++++++++++++++++++++++++++++++++
4 files changed, 936 insertions(+), 1 deletions(-)
@@ -0,0 +1,896 @@+/*+*FreescaleMPC5200PSCinI2Smode+*ALSASoCDigitalAudioInterface(DAI)driver+*+*Copyright(C)2008SecretLabTechnologiesLtd.+*/++#include<linux/init.h>+#include<linux/module.h>+#include<linux/interrupt.h>+#include<linux/device.h>+#include<linux/delay.h>+#include<linux/of_device.h>+#include<linux/of_platform.h>+#include<linux/dma-mapping.h>++#include<sound/core.h>+#include<sound/pcm.h>+#include<sound/pcm_params.h>+#include<sound/initval.h>+#include<sound/soc.h>+#include<sound/soc-of.h>++#include<sysdev/bestcomm/bestcomm.h>+#include<sysdev/bestcomm/gen_bd.h>+#include<asm/mpc52xx_psc.h>++MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");+MODULE_LICENSE("GPL");++/**+*PSC_I2S_RATES:sampleratessupportedbytheI2S+*+*ThisdrivercurrentlyonlysupportsthePSCrunninginI2Sslavemode,+*whichmeansthecodecdeterminesthesamplerate.Therefore,wetell+*ALSAthatwesupportallratesandletthecodecdriverdecidewhatrates+*arereallysupported.+*/+#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \+SNDRV_PCM_RATE_CONTINUOUS)++/**+*PSC_I2S_FORMATS:audioformatssupportedbythePSCI2Smode+*/+#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \+SNDRV_PCM_FMTBIT_S24_BE|SNDRV_PCM_FMTBIT_S24_BE|\+SNDRV_PCM_FMTBIT_S32_BE)++/**+*psc_i2s_stream-Dataspecifictoasinglestream(playbackorcapture)+*@active:flagindicatingifthestreamisactive+*@psc_i2s:pointerbacktoparentpsc_i2sdatastructure+*@bcom_task:bestcommtaskstructure+*@irq:irqnumberforbestcommtask+*@period_start:physicaladdressofstartofDMAregion+*@period_end:physicaladdressofendofDMAregion+*@period_next_pt:physicaladdressofnextDMAbuffertoenqueue+*@period_bytes:sizeofDMAperiodinbytes+*/+structpsc_i2s_stream{+intactive;+structpsc_i2s*psc_i2s;+structbcom_task*bcom_task;+intirq;+structsnd_pcm_substream*stream;+dma_addr_tperiod_start;+dma_addr_tperiod_end;+dma_addr_tperiod_next_pt;+dma_addr_tperiod_current_pt;+intperiod_bytes;+};++/**+*psc_i2s-Privatedriverdata+*@name:shortnameforthisdevice("PSC0","PSC1",etc)+*@psc_regs:pointertothePSC'sregisters+*@fifo_regs:pointertothePSC'sFIFOregisters+*@irq:IRQofthisPSC+*@dev:structdevicepointer+*@playback:thenumberofplaybackstreamsopened+*@capture:thenumberofcapturestreamsopened+*@dai:theCPUDAIforthisdevice+*@playback_stream:Playbackstreamcontextdata+*@capture_stream:Capturestreamcontextdata+*/+structpsc_i2s{+charname[32];+structmpc52xx_psc__iomem*psc_regs;+structmpc52xx_psc_fifo__iomem*fifo_regs;+unsignedintirq;+structdevice*dev;+structsnd_soc_cpu_daidai;+spinlock_tlock;++/* per-stream data */+structpsc_i2s_streamplayback_stream;+structpsc_i2s_streamcapture_stream;++/* Statistics */+struct{+intoverrun_count;+intunderrun_count;+}stats;+};++/*+*Interrupthandlers+*/+staticirqreturn_tpsc_i2s_status_irq(intirq,void*_psc_i2s)+{+structpsc_i2s*psc_i2s=_psc_i2s;+structmpc52xx_psc__iomem*regs=psc_i2s->psc_regs;+u16imr;+u16isr;++isr=in_be16(®s->mpc52xx_psc_isr);+imr=in_be16(®s->mpc52xx_psc_imr);++/* Playback underrun error */+if(isr&imr&MPC52xx_PSC_IMR_TXEMP)+psc_i2s->stats.underrun_count++;++/* Capture overrun error */+if(isr&imr&MPC52xx_PSC_IMR_ORERR)+psc_i2s->stats.overrun_count++;++out_8(®s->command,4<<4);/* reset the error status */++returnIRQ_HANDLED;+}++/**+*psc_i2s_bcom_enqueue_next_buffer-Enqueueanotheraudiobuffer+*@s:pointertostreamprivatedatastructure+*+*Enqueuesanotheraudioperiodbufferintothebestcommqueue.+*+*Note:Theroutinemustonlybecalledwhenthereisspaceavailablein+*thequeue.Otherwisetheenqueuewillfailandtheaudioringbuffer+*willgetoutofsync+*/+staticvoidpsc_i2s_bcom_enqueue_next_buffer(structpsc_i2s_stream*s)+{+structbcom_bd*bd;++/* Prepare and enqueue the next buffer descriptor */+bd=bcom_prepare_next_buffer(s->bcom_task);+bd->status=s->period_bytes;+bd->data[0]=s->period_next_pt;+bcom_submit_next_buffer(s->bcom_task,NULL);++/* Update for next period */+s->period_next_pt+=s->period_bytes;+if(s->period_next_pt>=s->period_end)+s->period_next_pt=s->period_start;+}++/* Bestcomm DMA irq handler */+staticirqreturn_tpsc_i2s_bcom_irq(intirq,void*_psc_i2s_stream)+{+structpsc_i2s_stream*s=_psc_i2s_stream;++//spin_lock(&s->psc_i2s->lock);++/* For each finished period, dequeue the completed period buffer+*andenqueueanewoneinit'splace.*/+while(bcom_buffer_done(s->bcom_task)){+bcom_retrieve_buffer(s->bcom_task,NULL,NULL);+s->period_current_pt+=s->period_bytes;+if(s->period_current_pt>=s->period_end)+s->period_current_pt=s->period_start;+psc_i2s_bcom_enqueue_next_buffer(s);+bcom_enable(s->bcom_task);+}++//spin_unlock(&s->psc_i2s->lock);++/* If the stream is active, then also inform the PCM middle layer+*oftheperiodfinishedevent.*/+if(s->active)+snd_pcm_period_elapsed(s->stream);++returnIRQ_HANDLED;+}++/**+*psc_i2s_startup:createanewsubstream+*+*Thisisthefirstfunctioncalledwhenastreamisopened.+*+*Ifthisisthefirststreamopen,thengrabtheIRQandprogrammostof+*thePSCregisters.+*/+staticintpsc_i2s_startup(structsnd_pcm_substream*substream)+{+intplayback_irq,capture_irq,rc;+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;+structmpc52xx_psc__iomem*regs=psc_i2s->psc_regs;+structmpc52xx_psc_fifo__iomem*fiforegs=psc_i2s->fifo_regs;++dev_dbg(psc_i2s->dev,"psc_i2s_startup(substream=%p)\n",substream);++/* Disable all interrupts and reset the PSC */+out_be16(®s->mpc52xx_psc_imr,0);+out_8(®s->command,3<<4);/* reset transmitter */+out_8(®s->command,2<<4);/* reset receiver */+out_8(®s->command,1<<4);/* reset mode */+out_8(®s->command,4<<4);/* reset error */++/* Default to CODEC8 mode */+out_be32(®s->sicr,+MPC52xx_PSC_SICR_DTS1|MPC52xx_PSC_SICR_I2S|+MPC52xx_PSC_SICR_CLKPOL|MPC52xx_PSC_SICR_SIM_CODEC_8);++/* First write: RxRdy (FIFO Alarm) generates receive FIFO interrupt */+/* Second write to mode: register Normal mode for non loopback */+out_8(®s->mode,0);+out_8(®s->mode,0);++/* Set the TX and RX fifo alarm thresholds */+out_be16(&fiforegs->rfalarm,0x100);/* set RFALARM level */+out_8(&fiforegs->rfcntl,0x4);/* set RFGRAN level (bytes) */+out_be16(&fiforegs->tfalarm,0x100);/* set TFALARM level */+out_8(&fiforegs->tfcntl,0x7);/* set TFGRAN level (bytes*4) */++/* Setup the IRQs */+playback_irq=bcom_get_task_irq(psc_i2s->playback_stream.bcom_task);+capture_irq=bcom_get_task_irq(psc_i2s->capture_stream.bcom_task);+rc=request_irq(psc_i2s->irq,&psc_i2s_status_irq,IRQF_SHARED,+"psc-i2s-status",psc_i2s);+rc|=request_irq(capture_irq,&psc_i2s_bcom_irq,IRQF_SHARED,+"psc-i2s-capture",&psc_i2s->capture_stream);+rc|=request_irq(playback_irq,&psc_i2s_bcom_irq,IRQF_SHARED,+"psc-i2s-playback",&psc_i2s->playback_stream);+if(rc){+free_irq(psc_i2s->irq,psc_i2s);+free_irq(capture_irq,&psc_i2s->capture_stream);+free_irq(playback_irq,&psc_i2s->playback_stream);+return-ENODEV;+}++return0;+}++staticintpsc_i2s_hw_params(structsnd_pcm_substream*substream,+structsnd_pcm_hw_params*params)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;+u32sicr;++dev_dbg(psc_i2s->dev,"%s(substream=%p) p_size=%i p_bytes=%i"+" periods=%i buffer_size=%i buffer_bytes=%i\n",+__FUNCTION__,substream,params_period_size(params),+params_period_bytes(params),params_periods(params),+params_buffer_size(params),params_buffer_bytes(params));++sicr=MPC52xx_PSC_SICR_DTS1|+MPC52xx_PSC_SICR_I2S|MPC52xx_PSC_SICR_CLKPOL;+switch(params_format(params)){+caseSNDRV_PCM_FORMAT_S8:+sicr|=MPC52xx_PSC_SICR_SIM_CODEC_8;+break;+caseSNDRV_PCM_FORMAT_S16_BE:+sicr|=MPC52xx_PSC_SICR_SIM_CODEC_16;+break;+caseSNDRV_PCM_FORMAT_S24_BE:+sicr|=MPC52xx_PSC_SICR_SIM_CODEC_24;+break;+caseSNDRV_PCM_FORMAT_S32_BE:+sicr|=MPC52xx_PSC_SICR_SIM_CODEC_32;+break;+default:+dev_dbg(psc_i2s->dev,"invalid format\n");+return-EINVAL;+}+out_be32(&psc_i2s->psc_regs->sicr,sicr);++snd_pcm_set_runtime_buffer(substream,&substream->dma_buffer);++return0;+}++staticintpsc_i2s_hw_free(structsnd_pcm_substream*substream)+{+snd_pcm_set_runtime_buffer(substream,NULL);+return0;+}++/**+*psc_i2s_trigger:startandstoptheDMAtransfer.+*+*ThisfunctioniscalledbyALSAtostart,stop,pause,andresumetheDMA+*transferofdata.+*/+staticintpsc_i2s_trigger(structsnd_pcm_substream*substream,intcmd)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;+structsnd_pcm_runtime*runtime=substream->runtime;+structpsc_i2s_stream*s;+structmpc52xx_psc__iomem*regs=psc_i2s->psc_regs;+u16imr;+u8psc_cmd;+longflags;++if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE)+s=&psc_i2s->capture_stream;+else+s=&psc_i2s->playback_stream;++dev_dbg(psc_i2s->dev,"psc_i2s_trigger(substream=%p, cmd=%i)"+" stream_id=%i\n",+substream,cmd,substream->pstr->stream);++switch(cmd){+caseSNDRV_PCM_TRIGGER_START:+s->period_bytes=frames_to_bytes(runtime,+runtime->period_size);+s->period_start=virt_to_phys(runtime->dma_area);+s->period_end=s->period_start++(s->period_bytes*runtime->periods);+s->period_next_pt=s->period_start;+s->period_current_pt=s->period_start;+s->active=1;++/* First; reset everything */+if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE){+out_8(®s->command,MPC52xx_PSC_RST_RX);+out_8(®s->command,MPC52xx_PSC_RST_ERR_STAT);+}else{+out_8(®s->command,MPC52xx_PSC_RST_TX);+out_8(®s->command,MPC52xx_PSC_RST_ERR_STAT);+}++/* Next, fill up the bestcomm bd queue and enable DMA.+*ThiswillbeginfillingthePSC'sfifo.*/+if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE)+bcom_gen_bd_rx_reset(s->bcom_task);+else+bcom_gen_bd_tx_reset(s->bcom_task);+while(!bcom_queue_full(s->bcom_task))+psc_i2s_bcom_enqueue_next_buffer(s);+bcom_enable(s->bcom_task);++/* Update interrupt enable settings. This must be done+*beforethePSCisenabledsothatTXunderrunevents+*arenotmissed.*/+imr=0;+if(psc_i2s->playback_stream.active)+imr|=MPC52xx_PSC_IMR_TXEMP;+if(psc_i2s->capture_stream.active)+imr|=MPC52xx_PSC_IMR_ORERR;+out_be16(®s->isr_imr.imr,imr);++/* Due to errata in the i2s mode; need to line up enabling+*thetransmitterwithatransitionontheframesync+*line*/++spin_lock_irqsave(&psc_i2s->lock,flags);+/* first make sure it is low */+while((in_8(®s->ipcr_acr.ipcr)&0x80)!=0);+/* then wait for the transition to high */+while((in_8(®s->ipcr_acr.ipcr)&0x80)==0);+/* Finally, enable the PSC.+*Receivermustalwaysbeenabled;evenwhenweonlywant+*transmit.(see15.3.2.3ofMPC5200BUser'sGuide)*/+psc_cmd=MPC52xx_PSC_RX_ENABLE;+if(substream->pstr->stream==SNDRV_PCM_STREAM_PLAYBACK)+psc_cmd|=MPC52xx_PSC_TX_ENABLE;+out_8(®s->command,psc_cmd);+spin_unlock_irqrestore(&psc_i2s->lock,flags);++break;++caseSNDRV_PCM_TRIGGER_STOP:+/* Turn off the PSC */+s->active=0;+if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE){+if(!psc_i2s->playback_stream.active){+out_8(®s->command,2<<4);/* reset rx */+out_8(®s->command,3<<4);/* reset tx */+out_8(®s->command,4<<4);/* reset err */+}+}else{+out_8(®s->command,3<<4);/* reset tx */+out_8(®s->command,4<<4);/* reset err */+if(!psc_i2s->capture_stream.active)+out_8(®s->command,2<<4);/* reset rx */+}++bcom_disable(s->bcom_task);+while(!bcom_queue_empty(s->bcom_task))+bcom_retrieve_buffer(s->bcom_task,NULL,NULL);++break;++default:+dev_dbg(psc_i2s->dev,"invalid command\n");+return-EINVAL;+}++/* Update interrupt enable settings */+imr=0;+if(psc_i2s->playback_stream.active)imr|=MPC52xx_PSC_IMR_TXEMP;+if(psc_i2s->capture_stream.active)imr|=MPC52xx_PSC_IMR_ORERR;+out_be16(®s->isr_imr.imr,imr);++return0;+}++/**+*psc_i2s_shutdown:shutdownthedatatransferonastream+*+*ShutdownthePSCiftherearenoothersubstreamsopen.+*/+staticvoidpsc_i2s_shutdown(structsnd_pcm_substream*substream)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;++dev_dbg(psc_i2s->dev,"psc_i2s_shutdown(substream=%p)\n",substream);++/*+*Ifthisisthelastactivesubstream,disablethePSCandrelease+*theIRQ.+*/+if(!psc_i2s->playback_stream.active&&+!psc_i2s->capture_stream.active){+/* TODO: shut off channels */+free_irq(psc_i2s->irq,psc_i2s);+free_irq(bcom_get_task_irq(psc_i2s->capture_stream.bcom_task),+&psc_i2s->capture_stream);+free_irq(bcom_get_task_irq(psc_i2s->playback_stream.bcom_task),+&psc_i2s->playback_stream);+}+}++/**+*psc_i2s_set_sysclk:settheclockfrequencyanddirection+*+*Thisfunctioniscalledbythemachinedrivertotelluswhattheclock+*frequencyanddirectionare.+*+*Currently,weonlysupportoperatingasaclockslave(SND_SOC_CLOCK_IN),+*andwedon'tcareaboutthefrequency.Returnanerrorifthedirection+*isnotSND_SOC_CLOCK_IN.+*+*@clk_id:reserved,shouldbezero+*@freq:thefrequencyofthegivenclockID,currentlyignored+*@dir:SND_SOC_CLOCK_IN(clockslave)orSND_SOC_CLOCK_OUT(clockmaster)+*/+staticintpsc_i2s_set_sysclk(structsnd_soc_cpu_dai*cpu_dai,+intclk_id,unsignedintfreq,intdir)+{+structpsc_i2s*psc_i2s=cpu_dai->private_data;+dev_dbg(psc_i2s->dev,"psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",+cpu_dai,dir);+return(dir==SND_SOC_CLOCK_IN)?0:-EINVAL;+}++/**+*psc_i2s_set_fmt:settheserialformat.+*+*Thisfunctioniscalledbythemachinedrivertotelluswhatserial+*formattouse.+*+*ThisdriveronlysupportsI2Smode.Returnanerroriftheformatis+*notSND_SOC_DAIFMT_I2S.+*+*@format:oneofSND_SOC_DAIFMT_xxx+*/+staticintpsc_i2s_set_fmt(structsnd_soc_cpu_dai*cpu_dai,unsignedintformat)+{+structpsc_i2s*psc_i2s=cpu_dai->private_data;+dev_dbg(psc_i2s->dev,"psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",+cpu_dai,format);+return(format==SND_SOC_DAIFMT_I2S)?0:-EINVAL;+}++/* ---------------------------------------------------------------------+*ALSASoCBindings+*+*-DigitalAudioInterface(DAI)template+*-create/destroydaihooks+*/++/**+*psc_i2s_dai_template:templateCPUDigitalAudioInterface+*/+staticstructsnd_soc_cpu_daipsc_i2s_dai_template={+.type=SND_SOC_DAI_I2S,+.playback={+.channels_min=2,+.channels_max=2,+.rates=PSC_I2S_RATES,+.formats=PSC_I2S_FORMATS,+},+.capture={+.channels_min=2,+.channels_max=2,+.rates=PSC_I2S_RATES,+.formats=PSC_I2S_FORMATS,+},+.ops={+.startup=psc_i2s_startup,+.hw_params=psc_i2s_hw_params,+.hw_free=psc_i2s_hw_free,+.shutdown=psc_i2s_shutdown,+.trigger=psc_i2s_trigger,+},+.dai_ops={+.set_sysclk=psc_i2s_set_sysclk,+.set_fmt=psc_i2s_set_fmt,+},+};++/* ---------------------------------------------------------------------+*ThePSCI2S'ASoCplatform'driver+*+*Canbereferencedbyan'ASoCmachine'driver+*Thisdriveronlydealswiththeaudiobus;itdoesn'thaveany+*interactionwiththeattachedcodec+*/++staticconststructsnd_pcm_hardwarepsc_i2s_pcm_hardware={+.info=SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|+SNDRV_PCM_INFO_INTERLEAVED|SNDRV_PCM_INFO_BLOCK_TRANSFER,+.formats=SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_BE|+SNDRV_PCM_FMTBIT_S24_BE|SNDRV_PCM_FMTBIT_S32_BE,+.rate_min=8000,+.rate_max=48000,+.channels_min=2,+.channels_max=2,+.period_bytes_max=1024*1024,+.period_bytes_min=32,+.period_bytes_max=1024*1024,+.periods_min=2,+.periods_max=256,+.buffer_bytes_max=2*1024*1024,+.fifo_size=0,+};++staticunsignedintpsc_i2s_fixed_rates[]={+8000,11025,12000,16000,22050,24000,32000,44100,48000,+};++staticstructsnd_pcm_hw_constraint_listpsc_i2s_constraints_rates={+.count=ARRAY_SIZE(psc_i2s_fixed_rates),+.list=psc_i2s_fixed_rates,+.mask=0,+};++staticintpsc_i2s_pcm_open(structsnd_pcm_substream*substream)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;+structpsc_i2s_stream*s;+intrc;++dev_dbg(psc_i2s->dev,"psc_i2s_pcm_open(substream=%p)\n",substream);++if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE)+s=&psc_i2s->capture_stream;+else+s=&psc_i2s->playback_stream;++snd_soc_set_runtime_hwparams(substream,&psc_i2s_pcm_hardware);++rc=snd_pcm_hw_constraint_integer(substream->runtime,+SNDRV_PCM_HW_PARAM_PERIODS);+if(rc<0){+dev_err(psc_i2s->dev,"invalid buffer size\n");+returnrc;+}+rc=snd_pcm_hw_constraint_list(substream->runtime,0,+SNDRV_PCM_HW_PARAM_RATE,+&psc_i2s_constraints_rates);+if(rc<0){+dev_err(psc_i2s->dev,"invalid rate\n");+returnrc;+}++s->stream=substream;+return0;+}++staticintpsc_i2s_pcm_close(structsnd_pcm_substream*substream)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;+structpsc_i2s_stream*s;++dev_dbg(psc_i2s->dev,"psc_i2s_pcm_close(substream=%p)\n",substream);++if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE)+s=&psc_i2s->capture_stream;+else+s=&psc_i2s->playback_stream;++s->stream=NULL;+return0;+}++staticsnd_pcm_uframes_t+psc_i2s_pcm_pointer(structsnd_pcm_substream*substream)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structpsc_i2s*psc_i2s=rtd->dai->cpu_dai->private_data;+structpsc_i2s_stream*s;+dma_addr_tcount;++if(substream->pstr->stream==SNDRV_PCM_STREAM_CAPTURE)+s=&psc_i2s->capture_stream;+else+s=&psc_i2s->playback_stream;++count=s->period_current_pt-s->period_start;++returnbytes_to_frames(substream->runtime,count);+}++staticstructsnd_pcm_opspsc_i2s_pcm_ops={+.open=psc_i2s_pcm_open,+.close=psc_i2s_pcm_close,+.ioctl=snd_pcm_lib_ioctl,+.pointer=psc_i2s_pcm_pointer,+};++staticu64psc_i2s_pcm_dmamask=0xffffffff;+staticintpsc_i2s_pcm_new(structsnd_card*card,structsnd_soc_codec_dai*dai,+structsnd_pcm*pcm)+{+structsnd_soc_pcm_runtime*rtd=pcm->private_data;+size_tsize=psc_i2s_pcm_hardware.buffer_bytes_max;+intrc=0;++dev_dbg(rtd->socdev->dev,"psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",+card,dai,pcm);++if(!card->dev->dma_mask)+card->dev->dma_mask=&psc_i2s_pcm_dmamask;+if(!card->dev->coherent_dma_mask)+card->dev->coherent_dma_mask=0xffffffff;++if(pcm->streams[0].substream){+rc=snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,pcm->dev,size,+&pcm->streams[0].substream->dma_buffer);+if(rc){+dev_err(card->dev,"Cannot alloc playback buffer\n");+return-ENOMEM;+}+}++if(pcm->streams[1].substream){+rc=snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,pcm->dev,size,+&pcm->streams[1].substream->dma_buffer);+if(rc){+if(pcm->streams[0].substream)+snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);+dev_err(card->dev,"Cannot allocate capture buffer\n");+return-ENOMEM;+}+}++return0;+}++staticvoidpsc_i2s_pcm_free(structsnd_pcm*pcm)+{+structsnd_soc_pcm_runtime*rtd=pcm->private_data;+structsnd_pcm_substream*substream;+intstream;++dev_dbg(rtd->socdev->dev,"psc_i2s_pcm_free(pcm=%p)\n",pcm);++for(stream=0;stream<2;stream++){+substream=pcm->streams[stream].substream;+if(substream){+snd_dma_free_pages(&substream->dma_buffer);+substream->dma_buffer.area=NULL;+substream->dma_buffer.addr=0;+}+}+}++structsnd_soc_platformpsc_i2s_pcm_soc_platform={+.name="mpc5200-psc-audio",+.pcm_ops=&psc_i2s_pcm_ops,+.pcm_new=&psc_i2s_pcm_new,+.pcm_free=&psc_i2s_pcm_free,+};++/* ---------------------------------------------------------------------+*Sysfsattributesfordebugging+*/++staticssize_tpsc_i2s_status_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structpsc_i2s*psc_i2s=dev_get_drvdata(dev);++returnsprintf(buf,"status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x tfnum=%i tfstat=0x%.4x\n",+in_be16(&psc_i2s->psc_regs->sr_csr.status),+in_be32(&psc_i2s->psc_regs->sicr),+in_be16(&psc_i2s->fifo_regs->rfnum)&0x1ff,+in_be16(&psc_i2s->fifo_regs->rfstat),+in_be16(&psc_i2s->fifo_regs->tfnum)&0x1ff,+in_be16(&psc_i2s->fifo_regs->tfstat));+}++staticint*psc_i2s_get_stat_attr(structpsc_i2s*psc_i2s,+constchar*name)+{+if(strcmp(name,"playback_underrun")==0)+return&psc_i2s->stats.underrun_count;+if(strcmp(name,"capture_overrun")==0)+return&psc_i2s->stats.overrun_count;++returnNULL;+}++staticssize_tpsc_i2s_stat_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structpsc_i2s*psc_i2s=dev_get_drvdata(dev);+int*attrib;++attrib=psc_i2s_get_stat_attr(psc_i2s,attr->attr.name);+if(!attrib)+return0;++returnsprintf(buf,"%i\n",*attrib);+}++staticssize_tpsc_i2s_stat_store(structdevice*dev,+structdevice_attribute*attr,+constchar*buf,+size_tcount)+{+structpsc_i2s*psc_i2s=dev_get_drvdata(dev);+int*attrib;++attrib=psc_i2s_get_stat_attr(psc_i2s,attr->attr.name);+if(!attrib)+return0;++*attrib=simple_strtoul(buf,NULL,0);+returncount;+}++DEVICE_ATTR(status,0644,psc_i2s_status_show,NULL);+DEVICE_ATTR(playback_underrun,0644,psc_i2s_stat_show,psc_i2s_stat_store);+DEVICE_ATTR(capture_overrun,0644,psc_i2s_stat_show,psc_i2s_stat_store);++/* ---------------------------------------------------------------------+*OFplatformbusbindingcode:+*-Probe/removeoperations+*-OFdevicematchtable+*/+staticint__devinitpsc_i2s_of_probe(structof_device*op,+conststructof_device_id*match)+{+phys_addr_tfifo;+structpsc_i2s*psc_i2s;+structresourceres;+intsize,psc_id,irq,rc;+const__be32*prop;+void__iomem*regs;++dev_dbg(&op->dev,"probing psc i2s device\n");++/* Get the PSC ID */+prop=of_get_property(op->node,"cell-index",&size);+if(!prop||size<sizeof*prop)+return-ENODEV;+psc_id=be32_to_cpu(*prop);++/* Fetch the registers and IRQ of the PSC */+irq=irq_of_parse_and_map(op->node,0);+if(of_address_to_resource(op->node,0,&res)){+dev_err(&op->dev,"Missing reg property\n");+return-ENODEV;+}+regs=ioremap(res.start,1+res.end-res.start);+if(!regs){+dev_err(&op->dev,"Could not map registers\n");+return-ENODEV;+}++/* Allocate and initialize the driver private data */+psc_i2s=kzalloc(sizeof*psc_i2s,GFP_KERNEL);+if(!psc_i2s){+iounmap(regs);+return-ENOMEM;+}+spin_lock_init(&psc_i2s->lock);+psc_i2s->irq=irq;+psc_i2s->psc_regs=regs;+psc_i2s->fifo_regs=regs+sizeof*psc_i2s->psc_regs;+psc_i2s->dev=&op->dev;+psc_i2s->playback_stream.psc_i2s=psc_i2s;+psc_i2s->capture_stream.psc_i2s=psc_i2s;+snprintf(psc_i2s->name,sizeofpsc_i2s->name,"PSC%u",psc_id+1);++/* Fill out the CPU DAI structure */+memcpy(&psc_i2s->dai,&psc_i2s_dai_template,sizeofpsc_i2s->dai);+psc_i2s->dai.private_data=psc_i2s;+psc_i2s->dai.name=psc_i2s->name;+psc_i2s->dai.id=psc_id;++/* Find the address of the fifo data registers and setup the+*DMAtasks*/+fifo=res.start+offsetof(structmpc52xx_psc,buffer.buffer_32);+psc_i2s->capture_stream.bcom_task=+bcom_psc_gen_bd_rx_init(psc_id,10,fifo,512);+psc_i2s->playback_stream.bcom_task=+bcom_psc_gen_bd_tx_init(psc_id,10,fifo);+if(!psc_i2s->capture_stream.bcom_task||+!psc_i2s->playback_stream.bcom_task){+dev_err(&op->dev,"Could not allocate bestcomm tasks\n");+iounmap(regs);+kfree(psc_i2s);+return-ENODEV;+}++/* Save what we've done so it can be found again later */+dev_set_drvdata(&op->dev,psc_i2s);++/* Register the SYSFS files */+rc=device_create_file(psc_i2s->dev,&dev_attr_status);+rc=device_create_file(psc_i2s->dev,&dev_attr_capture_overrun);+rc=device_create_file(psc_i2s->dev,&dev_attr_playback_underrun);+if(rc)+dev_info(psc_i2s->dev,"error creating sysfs files\n");++/* Tell the ASoC OF helpers about it */+of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform,op->node,+&psc_i2s->dai);++return0;+}++staticint__devexitpsc_i2s_of_remove(structof_device*op)+{+structpsc_i2s*psc_i2s=dev_get_drvdata(&op->dev);++dev_dbg(&op->dev,"psc_i2s_remove()\n");++bcom_gen_bd_rx_release(psc_i2s->capture_stream.bcom_task);+bcom_gen_bd_tx_release(psc_i2s->playback_stream.bcom_task);++iounmap(psc_i2s->psc_regs);+iounmap(psc_i2s->fifo_regs);+kfree(psc_i2s);+dev_set_drvdata(&op->dev,NULL);++return0;+}++/* Match table for of_platform binding */+staticstructof_device_idpsc_i2s_match[]__devinitdata={+{.compatible="fsl,mpc5200-psc-i2s",},+{}+};+MODULE_DEVICE_TABLE(of,psc_i2s_match);++staticstructof_platform_driverpsc_i2s_driver={+.match_table=psc_i2s_match,+.probe=psc_i2s_of_probe,+.remove=__devexit_p(psc_i2s_of_remove),+.driver={+.name="mpc5200-psc-i2s",+.owner=THIS_MODULE,+},+};++/* ---------------------------------------------------------------------+*Modulesetupandteardown;simplyregistertheof_platformdriver+*forthePSCinI2Smode.+*/+staticint__initpsc_i2s_init(void)+{+returnof_register_platform_driver(&psc_i2s_driver);+}+module_init(psc_i2s_init);++staticvoid__exitpsc_i2s_exit(void)+{+of_unregister_platform_driver(&psc_i2s_driver);+}+module_exit(psc_i2s_exit);++
From: Grant Likely <hidden> Date: 2008-07-12 08:39:42
From: Grant Likely <redacted>
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
---
sound/soc/codecs/Kconfig | 4
sound/soc/codecs/Makefile | 2
sound/soc/codecs/tlv320aic26.c | 546 ++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/tlv320aic26.h | 103 ++++++++
4 files changed, 655 insertions(+), 0 deletions(-)
@@ -0,0 +1,546 @@+/*+*TexasInstrumentsTLV320AIC26lowpoweraudioCODEC+*ALSASoCCODECdriver+*+*Copyright(C)2008SecretLabTechnologiesLtd.+*/++#include<linux/module.h>+#include<linux/moduleparam.h>+#include<linux/init.h>+#include<linux/delay.h>+#include<linux/pm.h>+#include<linux/device.h>+#include<linux/sysfs.h>+#include<linux/spi/spi.h>+#include<sound/core.h>+#include<sound/pcm.h>+#include<sound/pcm_params.h>+#include<sound/soc.h>+#include<sound/soc-dapm.h>+#include<sound/soc-of.h>+#include<sound/initval.h>++#include"tlv320aic26.h"++MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");+MODULE_LICENSE("GPL");++/* AIC26 driver private data */+structaic26{+structspi_device*spi;+structsnd_soc_codeccodec;+u16reg_cache[AIC26_REG_CACHE_SIZE];/* shadow registers */+intmaster;+intdatfm;+intmclk;++/* Keyclick parameters */+intkeyclick_amplitude;+intkeyclick_freq;+intkeyclick_len;+};++/* ---------------------------------------------------------------------+*Registeraccessroutines+*/+staticunsignedintaic26_reg_read(structsnd_soc_codec*codec,+unsignedintreg)+{+structaic26*aic26=codec->private_data;+u16*cache=codec->reg_cache;+u16cmd,value;+u8buffer[2];+intrc;++if(reg>=AIC26_NUM_REGS){+WARN_ON_ONCE(1);+return0;+}++/* Do SPI transfer; first 16bits are command; remaining is+*registercontents*/+cmd=AIC26_READ_COMMAND_WORD(reg);+buffer[0]=(cmd>>8)&0xff;+buffer[1]=cmd&0xff;+rc=spi_write_then_read(aic26->spi,buffer,2,buffer,2);+if(rc){+dev_err(&aic26->spi->dev,"AIC26 reg read error\n");+return-EIO;+}+value=(buffer[0]<<8)|buffer[1];++/* Update the cache before returning with the value */+if(AIC26_REG_IS_CACHED(reg))+cache[AIC26_REG_CACHE_ADDR(reg)]=value;+returnvalue;+}++staticunsignedintaic26_reg_read_cache(structsnd_soc_codec*codec,+unsignedintreg)+{+u16*cache=codec->reg_cache;++if((reg<0)||(reg>=AIC26_NUM_REGS)){+WARN_ON_ONCE(1);+return0;+}++if(AIC26_REG_IS_CACHED(reg))+returncache[AIC26_REG_CACHE_ADDR(reg)];++returnaic26_reg_read(codec,reg);+}++staticintaic26_reg_write(structsnd_soc_codec*codec,unsignedintreg,+unsignedintvalue)+{+structaic26*aic26=codec->private_data;+u16*cache=codec->reg_cache;+u16cmd;+u8buffer[4];+intrc;++if((reg<0)||(reg>=AIC26_NUM_REGS)){+WARN_ON_ONCE(1);+return-EINVAL;+}++/* Do SPI transfer; first 16bits are command; remaining is data+*towriteintoregister*/+cmd=AIC26_WRITE_COMMAND_WORD(reg);+buffer[0]=(cmd>>8)&0xff;+buffer[1]=cmd&0xff;+buffer[2]=value>>8;+buffer[3]=value;+rc=spi_write(aic26->spi,buffer,4);+if(rc){+dev_err(&aic26->spi->dev,"AIC26 reg read error\n");+return-EIO;+}++/* update cache before returning */+if(AIC26_REG_IS_CACHED(reg))+cache[AIC26_REG_CACHE_ADDR(reg)]=value;+return0;+}++/* ---------------------------------------------------------------------+*DigitalAudioInterfaceOperations+*/+staticintaic26_hw_params(structsnd_pcm_substream*substream,+structsnd_pcm_hw_params*params)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structsnd_soc_device*socdev=rtd->socdev;+structsnd_soc_codec*codec=socdev->codec;+structaic26*aic26=codec->private_data;+intfsref,divisor,wlen,pval,jval,dval,qval;+u16reg;++dev_dbg(&aic26->spi->dev,"aic26_hw_params(substream=%p, params=%p)\n",+substream,params);+dev_dbg(&aic26->spi->dev,"rate=%i format=%i\n",params_rate(params),+params_format(params));++switch(params_rate(params)){+case8000:fsref=48000;divisor=AIC26_DIV_6;break;+case11025:fsref=44100;divisor=AIC26_DIV_4;break;+case12000:fsref=48000;divisor=AIC26_DIV_4;break;+case16000:fsref=48000;divisor=AIC26_DIV_3;break;+case22050:fsref=44100;divisor=AIC26_DIV_2;break;+case24000:fsref=48000;divisor=AIC26_DIV_2;break;+case32000:fsref=48000;divisor=AIC26_DIV_1_5;break;+case44100:fsref=44100;divisor=AIC26_DIV_1;break;+case48000:fsref=48000;divisor=AIC26_DIV_1;break;+default:dev_dbg(&aic26->spi->dev,"bad rate\n");return-EINVAL;+}++/* select data word length */+switch(params_format(params)){+caseSNDRV_PCM_FORMAT_S8:wlen=AIC26_WLEN_16;break;+caseSNDRV_PCM_FORMAT_S16_BE:wlen=AIC26_WLEN_16;break;+caseSNDRV_PCM_FORMAT_S24_BE:wlen=AIC26_WLEN_24;break;+caseSNDRV_PCM_FORMAT_S32_BE:wlen=AIC26_WLEN_32;break;+default:dev_dbg(&aic26->spi->dev,"bad format\n");return-EINVAL;+}++/* Configure PLL */+pval=1;+jval=(fsref==44100)?7:8;+dval=(fsref==44100)?5264:1920;+qval=0;+reg=0x8000|qval<<11|pval<<8|jval<<2;+aic26_reg_write(codec,AIC26_REG_PLL_PROG1,reg);+reg=dval<<2;+aic26_reg_write(codec,AIC26_REG_PLL_PROG2,reg);++/* Power up CODEC */+aic26_reg_write(codec,AIC26_REG_POWER_CTRL,0);++/* Audio Control 3 (master mode, fsref rate) */+reg=aic26_reg_read_cache(codec,AIC26_REG_AUDIO_CTRL3);+reg&=~0xf800;+if(aic26->master)+reg|=0x0800;+if(fsref==48000)+reg|=0x2000;+aic26_reg_write(codec,AIC26_REG_AUDIO_CTRL3,reg);++/* Audio Control 1 (FSref divisor) */+reg=aic26_reg_read_cache(codec,AIC26_REG_AUDIO_CTRL1);+reg&=~0x0fff;+reg|=wlen|aic26->datfm|(divisor<<3)|divisor;+aic26_reg_write(codec,AIC26_REG_AUDIO_CTRL1,reg);++return0;+}++/**+*aic26_mute-Mutecontroltoreducenoisewhenchangingaudioformat+*/+staticintaic26_mute(structsnd_soc_codec_dai*dai,intmute)+{+structsnd_soc_codec*codec=dai->codec;+structaic26*aic26=codec->private_data;+u16reg=aic26_reg_read_cache(codec,AIC26_REG_DAC_GAIN);++dev_dbg(&aic26->spi->dev,"aic26_mute(dai=%p, mute=%i)\n",+dai,mute);++if(mute)+reg|=0x8080;+else+reg&=~0x8080;+aic26_reg_write(codec,AIC26_REG_DAC_GAIN,reg);++return0;+}++staticintaic26_set_sysclk(structsnd_soc_codec_dai*codec_dai,+intclk_id,unsignedintfreq,intdir)+{+structsnd_soc_codec*codec=codec_dai->codec;+structaic26*aic26=codec->private_data;++dev_dbg(&aic26->spi->dev,"aic26_set_sysclk(dai=%p, clk_id==%i,"+" freq=%i, dir=%i)\n",+codec_dai,clk_id,freq,dir);++/* MCLK needs to fall between 2MHz and 50 MHz */+if((freq<2000000)||(freq>50000000))+return-EINVAL;++aic26->mclk=freq;+return0;+}++staticintaic26_set_fmt(structsnd_soc_codec_dai*codec_dai,unsignedintfmt)+{+structsnd_soc_codec*codec=codec_dai->codec;+structaic26*aic26=codec->private_data;++dev_dbg(&aic26->spi->dev,"aic26_set_fmt(dai=%p, fmt==%i)\n",+codec_dai,fmt);++/* set master/slave audio interface */+switch(fmt&SND_SOC_DAIFMT_MASTER_MASK){+caseSND_SOC_DAIFMT_CBM_CFM:aic26->master=1;break;+caseSND_SOC_DAIFMT_CBS_CFS:aic26->master=0;break;+default:dev_dbg(&aic26->spi->dev,"bad master\n");return-EINVAL;+}++/* interface format */+switch(fmt&SND_SOC_DAIFMT_FORMAT_MASK){+caseSND_SOC_DAIFMT_I2S:aic26->datfm=AIC26_DATFM_I2S;break;+caseSND_SOC_DAIFMT_DSP_A:aic26->datfm=AIC26_DATFM_DSP;break;+caseSND_SOC_DAIFMT_RIGHT_J:aic26->datfm=AIC26_DATFM_RIGHTJ;break;+caseSND_SOC_DAIFMT_LEFT_J:aic26->datfm=AIC26_DATFM_LEFTJ;break;+default:dev_dbg(&aic26->spi->dev,"bad format\n");return-EINVAL;+}++return0;+}++/* ---------------------------------------------------------------------+*DigitalAudioInterfaceDefinition+*/+structsnd_soc_codec_daiaic26_dai={+.name="tlv320aic26",+.playback={+.stream_name="Playback",+.channels_min=2,+.channels_max=2,+.rates=AIC26_RATES,+.formats=AIC26_FORMATS,+},+.capture={+.stream_name="Capture",+.channels_min=2,+.channels_max=2,+.rates=AIC26_RATES,+.formats=AIC26_FORMATS,+},+.ops={+.hw_params=aic26_hw_params,+},+.dai_ops={+.digital_mute=aic26_mute,+.set_sysclk=aic26_set_sysclk,+.set_fmt=aic26_set_fmt,+},+};+EXPORT_SYMBOL_GPL(aic26_dai);++/* ---------------------------------------------------------------------+*ALSAcontrols+*/+staticconstchar*aic26_capture_src_text[]={"Mic","Aux"};+staticconststructsoc_enumaic26_capture_src_enum=+SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1,12,2,aic26_capture_src_text);++staticconststructsnd_kcontrol_newaic26_snd_controls[]={+/* Output */+SOC_DOUBLE("PCM Playback Volume",AIC26_REG_DAC_GAIN,8,0,0x7f,1),+SOC_DOUBLE("PCM Playback Switch",AIC26_REG_DAC_GAIN,15,7,1,1),+SOC_SINGLE("PCM Capture Volume",AIC26_REG_ADC_GAIN,8,0x7f,0),+SOC_SINGLE("PCM Capture Mute",AIC26_REG_ADC_GAIN,15,1,1),+SOC_SINGLE("Keyclick activate",AIC26_REG_AUDIO_CTRL2,15,0x1,0),+SOC_SINGLE("Keyclick amplitude",AIC26_REG_AUDIO_CTRL2,12,0x7,0),+SOC_SINGLE("Keyclick frequency",AIC26_REG_AUDIO_CTRL2,8,0x7,0),+SOC_SINGLE("Keyclick period",AIC26_REG_AUDIO_CTRL2,4,0xf,0),+SOC_ENUM("Capture Source",aic26_capture_src_enum),+};++/* ---------------------------------------------------------------------+*SoCCODECportionofdriver:probeandreleaseroutines+*/+staticintaic26_probe(structplatform_device*pdev)+{+structsnd_soc_device*socdev=platform_get_drvdata(pdev);+structsnd_soc_codec*codec;+structsnd_kcontrol*kcontrol;+structaic26*aic26;+inti,ret,err;++dev_info(&pdev->dev,"Probing AIC26 SoC CODEC driver\n");+dev_dbg(&pdev->dev,"socdev=%p\n",socdev);+dev_dbg(&pdev->dev,"codec_data=%p\n",socdev->codec_data);++/* Fetch the relevant aic26 private data here (it's already been+*storedinthe.codecpointer)*/+aic26=socdev->codec_data;+if(aic26==NULL){+dev_err(&pdev->dev,"aic26: missing codec pointer\n");+return-ENODEV;+}+codec=&aic26->codec;+socdev->codec=codec;++dev_dbg(&pdev->dev,"Registering PCMs, dev=%p, socdev->dev=%p\n",+&pdev->dev,socdev->dev);+/* register pcms */+ret=snd_soc_new_pcms(socdev,SNDRV_DEFAULT_IDX1,SNDRV_DEFAULT_STR1);+if(ret<0){+dev_err(&pdev->dev,"aic26: failed to create pcms\n");+return-ENODEV;+}++/* register controls */+dev_dbg(&pdev->dev,"Registering controls\n");+for(i=0;i<ARRAY_SIZE(aic26_snd_controls);i++){+kcontrol=snd_soc_cnew(&aic26_snd_controls[i],codec,NULL);+err=snd_ctl_add(codec->card,kcontrol);+WARN_ON(err<0);+}++/* CODEC is setup, we can register the card now */+dev_dbg(&pdev->dev,"Registering card\n");+ret=snd_soc_register_card(socdev);+if(ret<0){+dev_err(&pdev->dev,"aic26: failed to register card\n");+gotocard_err;+}+return0;++card_err:+snd_soc_free_pcms(socdev);+returnret;+}++staticintaic26_remove(structplatform_device*pdev)+{+structsnd_soc_device*socdev=platform_get_drvdata(pdev);+snd_soc_free_pcms(socdev);+return0;+}++structsnd_soc_codec_deviceaic26_soc_codec_dev={+.probe=aic26_probe,+.remove=aic26_remove,+};++/* ---------------------------------------------------------------------+*SPIdeviceportionofdriver:sysfsfilesfordebugging+*/++staticssize_taic26_regs_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structaic26*aic26=dev_get_drvdata(dev);+char*idx=buf;+intcache_flag,addr,page,i,reg;++cache_flag=(strcmp(attr->attr.name,"regs_cache")==0);++for(page=0;page<3;page++){+for(i=0;i<0x20;i++){+addr=AIC26_PAGE_ADDR(page,i);+if(i%8==0)+idx+=sprintf(idx,"%i:%.2i:",page,i);+if(cache_flag)+reg=aic26_reg_read_cache(&aic26->codec,addr);+else+reg=aic26_reg_read(&aic26->codec,addr);+idx+=sprintf(idx," %.4x",reg);+if(i%8==7)+idx+=sprintf(idx,"\n");+}+}+returnidx-buf;+}++staticssize_taic26_keyclick_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structaic26*aic26=dev_get_drvdata(dev);+intval,amp,freq,len;++val=aic26_reg_read_cache(&aic26->codec,AIC26_REG_AUDIO_CTRL2);+amp=(val>>12)&0x7;+freq=(125<<((val>>8)&0x7))>>1;+len=2*(1+((val>>4)&0xf));++returnsprintf(buf,"amp=%x freq=%iHz len=%iclks\n",amp,freq,len);+}++/* Any write to the keyclick attribute will trigger the keyclick */+staticssize_taic26_keyclick_set(structdevice*dev,+structdevice_attribute*attr,+constchar*buf,size_tcount)+{+structaic26*aic26=dev_get_drvdata(dev);+intval;++val=aic26_reg_read_cache(&aic26->codec,AIC26_REG_AUDIO_CTRL2);+val|=0x8000;+aic26_reg_write(&aic26->codec,AIC26_REG_AUDIO_CTRL2,val);++returncount;+}++DEVICE_ATTR(regs,0644,aic26_regs_show,NULL);+DEVICE_ATTR(regs_cache,0644,aic26_regs_show,NULL);+DEVICE_ATTR(keyclick,0644,aic26_keyclick_show,aic26_keyclick_set);++/* ---------------------------------------------------------------------+*SPIdeviceportionofdriver:probeandreleaseroutinesandSPI+*driverregistration.+*/+staticintaic26_spi_probe(structspi_device*spi)+{+structaic26*aic26;+intrc,i,reg;++dev_dbg(&spi->dev,"probing tlv320aic26 spi device\n");++/* Allocate driver data */+aic26=kzalloc(sizeof*aic26,GFP_KERNEL);+if(!aic26)+return-ENOMEM;++/* Initialize the driver data */+aic26->spi=spi;+dev_set_drvdata(&spi->dev,aic26);++/* Setup what we can in the codec structure so that the register+*accessfunctionswillworkasexpected.Morewillbefilled+*outwhenitisprobedbytheSoCCODECpartofthisdriver*/+aic26->codec.private_data=aic26;+aic26->codec.name="aic26";+aic26->codec.owner=THIS_MODULE;+aic26->codec.dai=&aic26_dai;+aic26->codec.num_dai=1;+aic26->codec.read=aic26_reg_read;+aic26->codec.write=aic26_reg_write;+aic26->master=1;+mutex_init(&aic26->codec.mutex);+INIT_LIST_HEAD(&aic26->codec.dapm_widgets);+INIT_LIST_HEAD(&aic26->codec.dapm_paths);+aic26->codec.reg_cache_size=sizeof(aic26->reg_cache);+aic26->codec.reg_cache=aic26->reg_cache;++/* Reset the codec to power on defaults */+aic26_reg_write(&aic26->codec,AIC26_REG_RESET,0xBB00);++/* Power up CODEC */+aic26_reg_write(&aic26->codec,AIC26_REG_POWER_CTRL,0);++/* Audio Control 3 (master mode, fsref rate) */+reg=aic26_reg_read(&aic26->codec,AIC26_REG_AUDIO_CTRL3);+reg&=~0xf800;+reg|=0x0800;/* set master mode */+aic26_reg_write(&aic26->codec,AIC26_REG_AUDIO_CTRL3,reg);++/* Fill page 2 register cache */+for(i=0;i<ARRAY_SIZE(aic26->reg_cache);i++)+aic26_reg_read(&aic26->codec,AIC26_PAGE_ADDR(2,i));++/* Register the sysfs files for debugging */+/* Create SysFS files */+rc=device_create_file(&spi->dev,&dev_attr_regs);+rc|=device_create_file(&spi->dev,&dev_attr_regs_cache);+rc|=device_create_file(&spi->dev,&dev_attr_keyclick);+if(rc)+dev_info(&spi->dev,"error creating sysfs files\n");++#if defined(CONFIG_SND_SOC_OF)+/* Tell the of_soc helper about this codec */+of_snd_soc_register_codec(&aic26_soc_codec_dev,aic26,&aic26_dai,+spi->dev.archdata.of_node);+#endif++dev_dbg(&spi->dev,"SPI device initialized\n");+return0;+}++staticintaic26_spi_remove(structspi_device*spi)+{+structaic26*aic26=dev_get_drvdata(&spi->dev);++kfree(aic26);++return0;+}++staticstructspi_driveraic26_spi={+.driver={+.name="tlv320aic26",+.owner=THIS_MODULE,+},+.probe=aic26_spi_probe,+.remove=aic26_spi_remove,+};++staticint__initaic26_init(void)+{+returnspi_register_driver(&aic26_spi);+}+module_init(aic26_init);++staticvoid__exitaic26_exit(void)+{+spi_unregister_driver(&aic26_spi);+}+module_exit(aic26_exit);
From: Mark Brown <hidden> Date: 2008-07-12 18:10:06
On Sat, Jul 12, 2008 at 02:39:39AM -0600, Grant Likely wrote:
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
I've not reviewed this revision of these drivers yet but I just wanted
to point out that there's absoluely no problem with merging v1 drivers -
so long as a driver uses the existing merged APIs there's no issue from
that point of view.
From: Grant Likely <hidden> Date: 2008-07-12 18:14:42
On Sat, Jul 12, 2008 at 12:10 PM, Mark Brown
[off-list ref] wrote:
On Sat, Jul 12, 2008 at 02:39:39AM -0600, Grant Likely wrote:
quoted
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
I've not reviewed this revision of these drivers yet but I just wanted
to point out that there's absoluely no problem with merging v1 drivers -
so long as a driver uses the existing merged APIs there's no issue from
that point of view.
Oops, I forgot to update my commit messages. I'll probably repost v3
of the series this evening and I'll fix this.
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
From: Mark Brown <hidden> Date: 2008-07-14 11:46:01
On Sat, Jul 12, 2008 at 02:39:39AM -0600, Grant Likely wrote:
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
This looks basically good - most of the issues below are nitpicky.
Does the PLL configuration not depend on the input clock frequency? You
have a set_sysclk() method to configure the MCLK supplied but the driver
never appears to reference the value anywhere.
+ /* Power up CODEC */
+ aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
As discussed this should probably just be removed from hw_params().
+static int aic26_set_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt)
+{
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break;
+ case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break;
+ case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
+ case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break;
+ default: dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
I'm having a hard time liking this indentation style. It's not an
obstacle to merging but it doesn't really help legibility - there's not
enough white space and once you have a non-standard line like the
default: one.
This configuration is also exposed via a sysfs file, including some of
the configurability. Exposing the information via sysfs isn't a
particular problem but allowing it to be written to without going
through ALSA seems wrong.
From: Mark Brown <hidden> Date: 2008-07-14 12:11:00
On Sat, Jul 12, 2008 at 02:39:34AM -0600, Grant Likely wrote:
This is an I2S bus driver for the MPC5200 PSC device. It is probably
will not be merged as-is because it uses v1 of the ASoC API, but I want
to get it out there for comments.
Looks basically good. A few things below, plus you probably want to run
checkpatch over it.
This doesn't handle the pause, suspend, resume or pause_release
triggers. If there's really nothing to do for those it should ignore
them, especially given the default: behaviour.
From: Mark Brown <hidden> Date: 2008-07-14 13:49:38
On Sat, Jul 12, 2008 at 02:39:29AM -0600, Grant Likely wrote:
Simple utility layer for creating ASoC machine instances based on data
in the OpenFirmware device tree. OF aware platform drivers and codec
drivers register themselves with this framework and the framework
automatically instantiates a machine driver.
Ideally someone from the PowerPC community would sign off on this -
given the nature and volume of discussion people obviously have very
strong opinions about how machine drivers should be done so I'd really
like to see some community buy in for something like this. From an ASoC
point of view if this works it's fine.
This is most likely temporary glue code to work around limitations in
the ASoC v1 framework. I expect ASoC v2 won't need this.
It will need some way of providing a machine driver, generic (like this
one) or otherwise.
+static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ /* Only register the device if both the codec and platform have
+ * been registered */
+ if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
+ return;
+
+ pr_info("platform<-->codec match achieved; registering machine\n");
So what this does is add an extremely simple machine driver which
matches up the first DAI on a single codec and platform with no facility
for setting up any configurable clocking or anything? This is fine in
so far as it goes but it's going to work for very few systems as it is -
most codecs will need some additional configuration. This could be
added later, though.
Given this it might be worth renaming it to something less generic and
perhaps pushing it down into an of directory below the main ASoC
directory to parallel existing machine drivers. I'm happy with the code
from an ASoC point of view.
From: Jon Smirl <hidden> Date: 2008-07-14 14:13:16
On 7/14/08, Mark Brown [off-list ref] wrote:
On Sat, Jul 12, 2008 at 02:39:29AM -0600, Grant Likely wrote:
> Simple utility layer for creating ASoC machine instances based on data
> in the OpenFirmware device tree. OF aware platform drivers and codec
> drivers register themselves with this framework and the framework
> automatically instantiates a machine driver.
Ideally someone from the PowerPC community would sign off on this -
given the nature and volume of discussion people obviously have very
strong opinions about how machine drivers should be done so I'd really
like to see some community buy in for something like this. From an ASoC
point of view if this works it's fine.
Grant is one of the core PowerPC developers. There's no big
disagreement on the driver model, there just isn't a clean solution
for building a PowerPC ASoC driver under ASoC v1. The ASoC v1 driver
model is simply not compatible with the PowerPC device tree model.
That's why all of this glue code is needed.
Hopefully we can get the driver model sorted out in v2. If the ASoC
driver model is fixed all of this glue code disappears.
The PowerPC side isn't without fault too. PowerPC still doesn't have a
good way to load the fabric/machine driver.
Which are we going to call it, fabric or machine? I had been working
on the Apple code in sound/aoa. It is called fabric in that code. The
equivalent driver is called machine in ASoC v1.
> This is most likely temporary glue code to work around limitations in
> the ASoC v1 framework. I expect ASoC v2 won't need this.
It will need some way of providing a machine driver, generic (like this
one) or otherwise.
> +static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
> +{
> + struct platform_device *pdev;
> + int rc;
> +
> + /* Only register the device if both the codec and platform have
> + * been registered */
> + if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
> + return;
> +
> + pr_info("platform<-->codec match achieved; registering machine\n");
So what this does is add an extremely simple machine driver which
matches up the first DAI on a single codec and platform with no facility
for setting up any configurable clocking or anything? This is fine in
so far as it goes but it's going to work for very few systems as it is -
most codecs will need some additional configuration. This could be
added later, though.
Given this it might be worth renaming it to something less generic and
perhaps pushing it down into an of directory below the main ASoC
directory to parallel existing machine drivers. I'm happy with the code
from an ASoC point of view.
From: Anton Vorontsov <hidden> Date: 2008-07-14 14:16:09
On Sat, Jul 12, 2008 at 02:39:29AM -0600, Grant Likely wrote:
From: Grant Likely <redacted>
Simple utility layer for creating ASoC machine instances based on data
in the OpenFirmware device tree. OF aware platform drivers and codec
drivers register themselves with this framework and the framework
automatically instantiates a machine driver.
This is most likely temporary glue code to work around limitations in
the ASoC v1 framework. I expect ASoC v2 won't need this.
---
[...]
quoted hunk
--- /dev/null+++ b/sound/soc/soc-of.c
It's quite inconvenient to spread the firmware-specific bits over the
whole kernel source tree. So, can we place this in driver/of/, just as
we did for i2c, spi and gpio?
quoted hunk
@@ -0,0 +1,171 @@+/*+ * OF helpers for ALSA SoC Layer+ *+ * Copyright (C) 2008, Secret Lab Technologies Ltd.+ */++#include <linux/module.h>+#include <linux/moduleparam.h>+#include <linux/init.h>+#include <linux/delay.h>+#include <linux/pm.h>+#include <linux/bitops.h>+#include <linux/platform_device.h>+#include <linux/of.h>+#include <sound/core.h>+#include <sound/pcm.h>+#include <sound/pcm_params.h>+#include <sound/soc.h>+#include <sound/soc-of.h>+#include <sound/initval.h>++MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");+MODULE_LICENSE("GPL");+MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");++static DEFINE_MUTEX(of_snd_soc_mutex);+static LIST_HEAD(of_snd_soc_device_list);+static int of_snd_soc_next_index;++struct of_snd_soc_device {+ int id;+ struct list_head list;+ struct snd_soc_device device;+ struct snd_soc_machine machine;+ struct snd_soc_dai_link dai_link;+ struct platform_device *pdev;+ struct device_node *platform_node;+ struct device_node *codec_node;+};++static struct snd_soc_ops of_snd_soc_ops = {+};++static struct of_snd_soc_device *+of_snd_soc_get_device(struct device_node *codec_node)+{+ struct of_snd_soc_device *of_soc;++ list_for_each_entry(of_soc, &of_snd_soc_device_list, list) {+ if (of_soc->codec_node == codec_node)+ return of_soc;+ }++ of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL);+ if (!of_soc)+ return NULL;++ /* Initialize the structure and add it to the global list */+ of_soc->codec_node = codec_node;+ of_soc->id = of_snd_soc_next_index++;+ of_soc->machine.dai_link = &of_soc->dai_link;+ of_soc->machine.num_links = 1;+ of_soc->device.machine = &of_soc->machine;+ of_soc->dai_link.ops = &of_snd_soc_ops;+ list_add(&of_soc->list, &of_snd_soc_device_list);++ return of_soc;+}++static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)+{+ struct platform_device *pdev;+ int rc;++ /* Only register the device if both the codec and platform have+ * been registered */+ if ((!of_soc->device.codec_data) || (!of_soc->platform_node))+ return;++ pr_info("platform<-->codec match achieved; registering machine\n");++ pdev = platform_device_alloc("soc-audio", of_soc->id);+ if (!pdev) {+ pr_err("of_soc: platform_device_alloc() failed\n");+ return;+ }++ pdev->dev.platform_data = of_soc;+ platform_set_drvdata(pdev, &of_soc->device);+ of_soc->device.dev = &pdev->dev;++ /* The ASoC device is complete; register it */+ rc = platform_device_add(pdev);+ if (rc) {+ pr_err("of_soc: platform_device_add() failed\n");+ return;+ }++}++int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,+ void *codec_data, struct snd_soc_codec_dai *dai,+ struct device_node *node)+{+ struct of_snd_soc_device *of_soc;+ int rc = 0;++ pr_info("registering ASoC codec driver: %s\n", node->full_name);++ mutex_lock(&of_snd_soc_mutex);+ of_soc = of_snd_soc_get_device(node);+ if (!of_soc) {+ rc = -ENOMEM;+ goto out;+ }++ /* Store the codec data */+ of_soc->device.codec_data = codec_data;+ of_soc->device.codec_dev = codec_dev;+ of_soc->dai_link.name = node->name;+ of_soc->dai_link.stream_name = node->name;+ of_soc->dai_link.codec_dai = dai;++ /* Now try to register the SoC device */+ of_snd_soc_register_device(of_soc);++ out:+ mutex_unlock(&of_snd_soc_mutex);+ return rc;+}+EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);++int of_snd_soc_register_platform(struct snd_soc_platform *platform,+ struct device_node *node,+ struct snd_soc_cpu_dai *cpu_dai)+{+ struct of_snd_soc_device *of_soc;+ struct device_node *codec_node;+ const phandle *handle;+ int len, rc = 0;++ pr_info("registering ASoC platform driver: %s\n", node->full_name);++ handle = of_get_property(node, "codec-handle", &len);+ if (!handle || len < sizeof(handle))+ return -ENODEV;+ codec_node = of_find_node_by_phandle(*handle);+ if (!codec_node)+ return -ENODEV;+ pr_info("looking for codec: %s\n", codec_node->full_name);++ mutex_lock(&of_snd_soc_mutex);+ of_soc = of_snd_soc_get_device(codec_node);+ if (!of_soc) {+ rc = -ENOMEM;+ goto out;+ }++ of_soc->platform_node = node;+ of_soc->dai_link.cpu_dai = cpu_dai;+ of_soc->device.platform = platform;+ of_soc->machine.name = of_soc->dai_link.cpu_dai->name;++ /* Now try to register the SoC device */+ of_snd_soc_register_device(of_soc);++ out:+ mutex_unlock(&of_snd_soc_mutex);+ return rc;+}+EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
From: Mark Brown <hidden> Date: 2008-07-14 15:05:35
On Mon, Jul 14, 2008 at 10:13:14AM -0400, Jon Smirl wrote:
On 7/14/08, Mark Brown [off-list ref] wrote:
quoted
Ideally someone from the PowerPC community would sign off on this -
given the nature and volume of discussion people obviously have very
Grant is one of the core PowerPC developers. There's no big
OK, fair enough...
Hopefully we can get the driver model sorted out in v2. If the ASoC
driver model is fixed all of this glue code disappears.
The PowerPC side isn't without fault too. PowerPC still doesn't have a
good way to load the fabric/machine driver.
I'm finding it difficult to square these two statements - from an ASoC
point of view the main thing this patch is doing is adding a machine
driver and that's not something that's going to go away. With version 2
you will get the wait for all components to come on-line logic that's
implemented here from the ASoC core but that doesn't remove the need for
a machine driver to tell the core what to wait for and arrange any
machine specific things like clocking. It's this debate about machine
drivers that makes me nervous here.
Like I say, from an ASoC point of view it's not an issue and the current
approach is fine.
Which are we going to call it, fabric or machine? I had been working
on the Apple code in sound/aoa. It is called fabric in that code. The
equivalent driver is called machine in ASoC v1.
ASoC has always called it a machine driver.
quoted
> This is most likely temporary glue code to work around limitations in
> the ASoC v1 framework. I expect ASoC v2 won't need this.
It will need some way of providing a machine driver, generic (like this
one) or otherwise.
[BTW, it'd be helpful if you could delete unreferenced quotes when you
reply - it makes things much easier to read, especially when reviewing
lengthy patches. Unfortunately the GMail UI encourages doing this :(]
From: Timur Tabi <hidden> Date: 2008-07-14 16:14:56
Mark Brown wrote:
quoted
The PowerPC side isn't without fault too. PowerPC still doesn't have a
good way to load the fabric/machine driver.
I'm finding it difficult to square these two statements - from an ASoC
point of view the main thing this patch is doing is adding a machine
driver and that's not something that's going to go away.
Jon's concern is that there is no straightforward way to build a kernel with
multiple fabric drivers and have the right one chosen via the device tree. This
is just a limitation of the device tree model, and no one has come up with a
good solution yet.
The problem still exists in ASoC V2. However, it's not anything that ASoC
itself needs to be concerned with. It's purely a PowerPC problem.
quoted
Which are we going to call it, fabric or machine? I had been working
on the Apple code in sound/aoa. It is called fabric in that code. The
equivalent driver is called machine in ASoC v1.
ASoC has always called it a machine driver.
Wait, I thought it's supposed to be called a fabric driver now? On PowerPC, it
should be called a fabric driver because we already have machine drivers.
--
Timur Tabi
Linux kernel developer at Freescale
From: Grant Likely <hidden> Date: 2008-07-14 16:27:45
On Mon, Jul 14, 2008 at 10:14 AM, Timur Tabi [off-list ref] wrote:
Mark Brown wrote:
quoted
quoted
The PowerPC side isn't without fault too. PowerPC still doesn't have a
good way to load the fabric/machine driver.
I'm finding it difficult to square these two statements - from an ASoC
point of view the main thing this patch is doing is adding a machine
driver and that's not something that's going to go away.
Jon's concern is that there is no straightforward way to build a kernel with
multiple fabric drivers and have the right one chosen via the device tree. This
is just a limitation of the device tree model, and no one has come up with a
good solution yet.
The problem still exists in ASoC V2. However, it's not anything that ASoC
itself needs to be concerned with. It's purely a PowerPC problem.
It's purely an *OF Device Tree* problem. PowerPC isn't the only
platform using the device tree. :-)
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
From: Mark Brown <hidden> Date: 2008-07-14 16:53:13
On Mon, Jul 14, 2008 at 11:14:41AM -0500, Timur Tabi wrote:
Mark Brown wrote:
quoted
I'm finding it difficult to square these two statements - from an ASoC
point of view the main thing this patch is doing is adding a machine
driver and that's not something that's going to go away.
Jon's concern is that there is no straightforward way to build a kernel with
multiple fabric drivers and have the right one chosen via the device tree. This
is just a limitation of the device tree model, and no one has come up with a
good solution yet.
Indeed - I understand what the problem you guys have is, I just want to
make sure that there is a reasonable consensus among the PowerPC people
that this approach is OK to go in and won't create ructions. The lack
of resolution on this issue makes me nervous about any proposed solution
where I haven't seen any explicit indication that the community is OK
with it.
Incidentally, nobody ever really commented on my suggestion to do
something DMI-like - you've already got the board type information
present in the device trees (in the model and compatible information in
the root nodes), all that's needed is an API to allow matching on it.
The problem still exists in ASoC V2. However, it's not anything that ASoC
itself needs to be concerned with. It's purely a PowerPC problem.
Right, I just want to be clear that you guys all understand what this
code does and that there won't be too many complaints after the fact.
quoted
ASoC has always called it a machine driver.
Wait, I thought it's supposed to be called a fabric driver now? On PowerPC, it
should be called a fabric driver because we already have machine drivers.
I don't mind - you can call it what you like inside PowerPC-specific
code.
From: Grant Likely <hidden> Date: 2008-07-14 17:06:36
On Mon, Jul 14, 2008 at 7:49 AM, Mark Brown
[off-list ref] wrote:
On Sat, Jul 12, 2008 at 02:39:29AM -0600, Grant Likely wrote:
quoted
+static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ /* Only register the device if both the codec and platform have
+ * been registered */
+ if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
+ return;
+
+ pr_info("platform<-->codec match achieved; registering machine\n");
So what this does is add an extremely simple machine driver which
matches up the first DAI on a single codec and platform with no facility
for setting up any configurable clocking or anything? This is fine in
so far as it goes but it's going to work for very few systems as it is -
most codecs will need some additional configuration. This could be
added later, though.
Yes, that is pretty much exactly what it is. When I wrote this my
goal was just to get something working that had some semblance of
cleanliness. Since it is now looking like something that could get
merged, I'll clean up the documentation describing exactly what it is
and what the limitations are. I fully expect that if others find it
useful then they will need to extend it to extract additional
configuration information out of the device tree.
That being said, I expect this code to be completely rewritten when
ASoC v2 hits mainline. Most of the code is devoted to matching
platforms with codecs. The mechanism will be completely different
when the ASoC layer handles making sure all devices are present before
setting up the machine driver. I expect that some machines can be
handled with some form of generic of-asoc driver, while more complex
ones will need custom code.
Given this it might be worth renaming it to something less generic and
perhaps pushing it down into an of directory below the main ASoC
directory to parallel existing machine drivers. I'm happy with the code
from an ASoC point of view.
I'm okay with that. How about fsl/mpc5200-of-machine.c for now?
(only the mpc5200 i2s driver uses it at the moment). It can always be
renamed if other folks want to use it for other chips.
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
From: Grant Likely <hidden> Date: 2008-07-14 17:11:29
On Mon, Jul 14, 2008 at 8:16 AM, Anton Vorontsov
[off-list ref] wrote:
On Sat, Jul 12, 2008 at 02:39:29AM -0600, Grant Likely wrote:
quoted
--- /dev/null+++ b/sound/soc/soc-of.c
It's quite inconvenient to spread the firmware-specific bits over the
whole kernel source tree. So, can we place this in driver/of/, just as
we did for i2c, spi and gpio?
This isn't any more specific than say how device drivers are specific
for particular devices. This is an ASoC machine driver and should
live with all the other ASoC machine drivers.
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
From: Mark Brown <hidden> Date: 2008-07-14 17:16:39
On Mon, Jul 14, 2008 at 11:06:34AM -0600, Grant Likely wrote:
I'm okay with that. How about fsl/mpc5200-of-machine.c for now?
(only the mpc5200 i2s driver uses it at the moment). It can always be
renamed if other folks want to use it for other chips.
That seems reasonable so long as you're happy with it, though it does
seem to be going to the other extreme in terms of broadness :)
From: Grant Likely <hidden> Date: 2008-07-14 17:21:15
On Mon, Jul 14, 2008 at 10:53 AM, Mark Brown
[off-list ref] wrote:
On Mon, Jul 14, 2008 at 11:14:41AM -0500, Timur Tabi wrote:
quoted
Mark Brown wrote:
quoted
quoted
I'm finding it difficult to square these two statements - from an ASoC
point of view the main thing this patch is doing is adding a machine
driver and that's not something that's going to go away.
quoted
Jon's concern is that there is no straightforward way to build a kernel with
multiple fabric drivers and have the right one chosen via the device tree. This
is just a limitation of the device tree model, and no one has come up with a
good solution yet.
Indeed - I understand what the problem you guys have is, I just want to
make sure that there is a reasonable consensus among the PowerPC people
that this approach is OK to go in and won't create ructions. The lack
of resolution on this issue makes me nervous about any proposed solution
where I haven't seen any explicit indication that the community is OK
with it.
Incidentally, nobody ever really commented on my suggestion to do
something DMI-like
I'm feeling stupid; what does "DMI" stand for?
- you've already got the board type information
present in the device trees (in the model and compatible information in
the root nodes), all that's needed is an API to allow matching on it.
Yes, we have APIs for matching against device trees. Personally, I'm
leaning towards having the powerpc platform code
(arch/powerpc/platforms/* stuff; not ASoC platform stuff) register a
platform device for the machine driver and let as many machine drivers
as needed be written. Hopefully we'll be able to do at least one
generic machine driver that will be usable by most PowerPC boards, but
I don't think it is a requirement or even realistic to shoehorn all
powerpc sound circuits into a single driver.
quoted
The problem still exists in ASoC V2. However, it's not anything that ASoC
itself needs to be concerned with. It's purely a PowerPC problem.
Right, I just want to be clear that you guys all understand what this
code does and that there won't be too many complaints after the fact.
Shouldn't be. I'm certainly not pushing this as the end-all be-all
powerpc sound machine driver.
quoted
quoted
ASoC has always called it a machine driver.
quoted
Wait, I thought it's supposed to be called a fabric driver now? On PowerPC, it
should be called a fabric driver because we already have machine drivers.
I don't mind - you can call it what you like inside PowerPC-specific
code.
Oh help! Don't tell us that! Otherwise we'll always be talking
across purposes. When ambiguous, let's just be sure to always refer
to them as "ASoC machine drivers". :-)
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
From: Grant Likely <hidden> Date: 2008-07-14 17:22:08
On Mon, Jul 14, 2008 at 11:16 AM, Mark Brown
[off-list ref] wrote:
On Mon, Jul 14, 2008 at 11:06:34AM -0600, Grant Likely wrote:
quoted
I'm okay with that. How about fsl/mpc5200-of-machine.c for now?
(only the mpc5200 i2s driver uses it at the moment). It can always be
renamed if other folks want to use it for other chips.
That seems reasonable so long as you're happy with it, though it does
seem to be going to the other extreme in terms of broadness :)
heh; I don't care. This stuff is all going to be in flux anyway, so
change is inevitable whatever is chosen now. :-)
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
From: Mark Brown <hidden> Date: 2008-07-14 18:36:41
On Mon, Jul 14, 2008 at 11:21:12AM -0600, Grant Likely wrote:
On Mon, Jul 14, 2008 at 10:53 AM, Mark Brown
quoted
Incidentally, nobody ever really commented on my suggestion to do
something DMI-like
I'm feeling stupid; what does "DMI" stand for?
Desktop Management Interface, a standard BIOS interface for getting
system data on x86 class hardware. Of particular interest here is the
fact that it contains various ID strings for things like motherboard and
chassis - on Linux drivers can be automatically loaded based on these
strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
that does this.
Note that it's *not* binding a driver, it just provides the hooks to
enable the modules to be automatically loaded and to let drivers query
information in DMI.
Yes, we have APIs for matching against device trees. Personally, I'm
leaning towards having the powerpc platform code
(arch/powerpc/platforms/* stuff; not ASoC platform stuff) register a
platform device for the machine driver and let as many machine drivers
as needed be written. Hopefully we'll be able to do at least one
That would be what I'd expected initially - it is, after all, what other
platforms are doing here.
generic machine driver that will be usable by most PowerPC boards, but
I don't think it is a requirement or even realistic to shoehorn all
powerpc sound circuits into a single driver.
Yes, that'd be completely unrealistic.
quoted
I don't mind - you can call it what you like inside PowerPC-specific
code.
Oh help! Don't tell us that! Otherwise we'll always be talking
across purposes. When ambiguous, let's just be sure to always refer
to them as "ASoC machine drivers". :-)
Feh, from now on I shall me naming all new concepts with pwgen :) .
From: Timur Tabi <hidden> Date: 2008-07-14 18:40:34
Mark Brown wrote:
Desktop Management Interface, a standard BIOS interface for getting
system data on x86 class hardware. Of particular interest here is the
fact that it contains various ID strings for things like motherboard and
chassis - on Linux drivers can be automatically loaded based on these
strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
that does this.
The only problem with this is that the OF probing code in the kernel binds
drivers to device tree nodes. So when a driver claims a node, no other driver
will be probed with it.
So we can't have generic nodes that classify the motherboard and just let
everyone get probed on it.
--
Timur Tabi
Linux kernel developer at Freescale
From: Mark Brown <hidden> Date: 2008-07-14 18:49:37
On Mon, Jul 14, 2008 at 01:40:24PM -0500, Timur Tabi wrote:
Mark Brown wrote:
quoted
Desktop Management Interface, a standard BIOS interface for getting
system data on x86 class hardware. Of particular interest here is the
fact that it contains various ID strings for things like motherboard and
chassis - on Linux drivers can be automatically loaded based on these
strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
that does this.
The only problem with this is that the OF probing code in the kernel binds
drivers to device tree nodes. So when a driver claims a node, no other driver
will be probed with it.
So we can't have generic nodes that classify the motherboard and just let
everyone get probed on it.
My suggestion is that you change this for the root node. It's already
got the information required in there, it's just there's no way to use
it to load modules at the minute. You could presumably read the
information out of the device tree using existing APIs to check you're
running on the right board once code is loaded?
From: Timur Tabi <hidden> Date: 2008-07-14 18:53:45
Mark Brown wrote:
quoted
The only problem with this is that the OF probing code in the kernel binds
drivers to device tree nodes. So when a driver claims a node, no other driver
will be probed with it.
quoted
So we can't have generic nodes that classify the motherboard and just let
everyone get probed on it.
My suggestion is that you change this for the root node.
That's an interesting idea.
It's already
got the information required in there, it's just there's no way to use
it to load modules at the minute.
Correct.
You could presumably read the
information out of the device tree using existing APIs to check you're
running on the right board once code is loaded?
Yes. The driver <-> node binding is only for probing. Any driver can scan the
entire tree at any time.
--
Timur Tabi
Linux kernel developer at Freescale
From: Grant Likely <hidden> Date: 2008-07-14 22:28:12
On Mon, Jul 14, 2008 at 01:40:24PM -0500, Timur Tabi wrote:
Mark Brown wrote:
quoted
Desktop Management Interface, a standard BIOS interface for getting
system data on x86 class hardware. Of particular interest here is the
fact that it contains various ID strings for things like motherboard and
chassis - on Linux drivers can be automatically loaded based on these
strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
that does this.
The only problem with this is that the OF probing code in the kernel binds
drivers to device tree nodes. So when a driver claims a node, no other driver
will be probed with it.
Yes, but that is only for the of_platform bus which is just one way to
setup device drivers from the device tree. Setting it up from the
platform code is just as valid an option. Or, each ASoC machine driver could
read the device tree itself in the module_init hook to decide whether or not
to instantiate itself.
Cheers,
g.
From: Jon Smirl <hidden> Date: 2008-07-14 23:45:48
On 7/14/08, Timur Tabi [off-list ref] wrote:
Mark Brown wrote:
> Desktop Management Interface, a standard BIOS interface for getting
> system data on x86 class hardware. Of particular interest here is the
> fact that it contains various ID strings for things like motherboard and
> chassis - on Linux drivers can be automatically loaded based on these
> strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
> that does this.
The only problem with this is that the OF probing code in the kernel binds
drivers to device tree nodes. So when a driver claims a node, no other driver
will be probed with it.
So we can't have generic nodes that classify the motherboard and just let
everyone get probed on it.
Allowing multiple binds at the root causes the problem of something
like compatible="lite5200b,mpc5200-simple". Both platforms would bind
and that's not what you want.
I'm not a fan of each platform creating a platform device in
arch/powerpc/platforms/*. That implies that each platform would cause
another source file to be added each containing pretty much identical
code. It also makes the mpc5200-simple platform pointless. Of course
this scheme works and I'm doing it right now, but it's clearly not an
optimal solution.
Another scheme would be to add kernel code to always create virtual OF
devices like "lite5200b-fabric" that are derived off from the machine
name that achieved a bind.
A third scheme would be to convert the powerpc machine drivers
themselves to device drivers and move them out of
arch/powerpc/platforms/* into drivers/whatever. Then add the ASoC
fabric code to them. I don't know if you can load device drivers early
enough to load a powerpc machine driver from initrd.
A fourth scheme is to do it at compile time. But that means no
universal firmware images that support multiple hardware revisions. We
have this one today too.
--
Jon Smirl
jonsmirl@gmail.com
Hi
I have one query about this soc driver.
I want to know when u will merge it with kernel then
where and by which name this device will be available
in /dev directory of your file system.
As i am following the same structure for my driver so please help me. I
am ot able to recognise the device in the file system.
-----Original Message-----
From: Grant Likely <redacted>
To: linuxppc-dev@ozlabs.org, alsa-devel@alsa-project.org,
liam.girdwood@wolfsonmicro.com, jonsmirl@gmail.com
Subject: [alsa-devel] [PATCH v2 3/3] ALSA SoC: Add Texas Instruments
TLV320AIC26 codec driver
Date: Sat, 12 Jul 2008 02:39:39 -0600
From: Grant Likely <redacted>
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
---
sound/soc/codecs/Kconfig | 4
sound/soc/codecs/Makefile | 2
sound/soc/codecs/tlv320aic26.c | 546 ++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/tlv320aic26.h | 103 ++++++++
4 files changed, 655 insertions(+), 0 deletions(-)
@@ -0,0 +1,546 @@+/*+*TexasInstrumentsTLV320AIC26lowpoweraudioCODEC+*ALSASoCCODECdriver+*+*Copyright(C)2008SecretLabTechnologiesLtd.+*/++#include<linux/module.h>+#include<linux/moduleparam.h>+#include<linux/init.h>+#include<linux/delay.h>+#include<linux/pm.h>+#include<linux/device.h>+#include<linux/sysfs.h>+#include<linux/spi/spi.h>+#include<sound/core.h>+#include<sound/pcm.h>+#include<sound/pcm_params.h>+#include<sound/soc.h>+#include<sound/soc-dapm.h>+#include<sound/soc-of.h>+#include<sound/initval.h>++#include"tlv320aic26.h"++MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");+MODULE_LICENSE("GPL");++/* AIC26 driver private data */+structaic26{+structspi_device*spi;+structsnd_soc_codeccodec;+u16reg_cache[AIC26_REG_CACHE_SIZE];/* shadow registers */+intmaster;+intdatfm;+intmclk;++/* Keyclick parameters */+intkeyclick_amplitude;+intkeyclick_freq;+intkeyclick_len;+};++/* ---------------------------------------------------------------------+*Registeraccessroutines+*/+staticunsignedintaic26_reg_read(structsnd_soc_codec*codec,+unsignedintreg)+{+structaic26*aic26=codec->private_data;+u16*cache=codec->reg_cache;+u16cmd,value;+u8buffer[2];+intrc;++if(reg>=AIC26_NUM_REGS){+WARN_ON_ONCE(1);+return0;+}++/* Do SPI transfer; first 16bits are command; remaining is+*registercontents*/+cmd=AIC26_READ_COMMAND_WORD(reg);+buffer[0]=(cmd>>8)&0xff;+buffer[1]=cmd&0xff;+rc=spi_write_then_read(aic26->spi,buffer,2,buffer,2);+if(rc){+dev_err(&aic26->spi->dev,"AIC26 reg read error\n");+return-EIO;+}+value=(buffer[0]<<8)|buffer[1];++/* Update the cache before returning with the value */+if(AIC26_REG_IS_CACHED(reg))+cache[AIC26_REG_CACHE_ADDR(reg)]=value;+returnvalue;+}++staticunsignedintaic26_reg_read_cache(structsnd_soc_codec*codec,+unsignedintreg)+{+u16*cache=codec->reg_cache;++if((reg<0)||(reg>=AIC26_NUM_REGS)){+WARN_ON_ONCE(1);+return0;+}++if(AIC26_REG_IS_CACHED(reg))+returncache[AIC26_REG_CACHE_ADDR(reg)];++returnaic26_reg_read(codec,reg);+}++staticintaic26_reg_write(structsnd_soc_codec*codec,unsignedintreg,+unsignedintvalue)+{+structaic26*aic26=codec->private_data;+u16*cache=codec->reg_cache;+u16cmd;+u8buffer[4];+intrc;++if((reg<0)||(reg>=AIC26_NUM_REGS)){+WARN_ON_ONCE(1);+return-EINVAL;+}++/* Do SPI transfer; first 16bits are command; remaining is data+*towriteintoregister*/+cmd=AIC26_WRITE_COMMAND_WORD(reg);+buffer[0]=(cmd>>8)&0xff;+buffer[1]=cmd&0xff;+buffer[2]=value>>8;+buffer[3]=value;+rc=spi_write(aic26->spi,buffer,4);+if(rc){+dev_err(&aic26->spi->dev,"AIC26 reg read error\n");+return-EIO;+}++/* update cache before returning */+if(AIC26_REG_IS_CACHED(reg))+cache[AIC26_REG_CACHE_ADDR(reg)]=value;+return0;+}++/* ---------------------------------------------------------------------+*DigitalAudioInterfaceOperations+*/+staticintaic26_hw_params(structsnd_pcm_substream*substream,+structsnd_pcm_hw_params*params)+{+structsnd_soc_pcm_runtime*rtd=substream->private_data;+structsnd_soc_device*socdev=rtd->socdev;+structsnd_soc_codec*codec=socdev->codec;+structaic26*aic26=codec->private_data;+intfsref,divisor,wlen,pval,jval,dval,qval;+u16reg;++dev_dbg(&aic26->spi->dev,"aic26_hw_params(substream=%p, params=%p)\n",+substream,params);+dev_dbg(&aic26->spi->dev,"rate=%i format=%i\n",params_rate(params),+params_format(params));++switch(params_rate(params)){+case8000:fsref=48000;divisor=AIC26_DIV_6;break;+case11025:fsref=44100;divisor=AIC26_DIV_4;break;+case12000:fsref=48000;divisor=AIC26_DIV_4;break;+case16000:fsref=48000;divisor=AIC26_DIV_3;break;+case22050:fsref=44100;divisor=AIC26_DIV_2;break;+case24000:fsref=48000;divisor=AIC26_DIV_2;break;+case32000:fsref=48000;divisor=AIC26_DIV_1_5;break;+case44100:fsref=44100;divisor=AIC26_DIV_1;break;+case48000:fsref=48000;divisor=AIC26_DIV_1;break;+default:dev_dbg(&aic26->spi->dev,"bad rate\n");return-EINVAL;+}++/* select data word length */+switch(params_format(params)){+caseSNDRV_PCM_FORMAT_S8:wlen=AIC26_WLEN_16;break;+caseSNDRV_PCM_FORMAT_S16_BE:wlen=AIC26_WLEN_16;break;+caseSNDRV_PCM_FORMAT_S24_BE:wlen=AIC26_WLEN_24;break;+caseSNDRV_PCM_FORMAT_S32_BE:wlen=AIC26_WLEN_32;break;+default:dev_dbg(&aic26->spi->dev,"bad format\n");return-EINVAL;+}++/* Configure PLL */+pval=1;+jval=(fsref==44100)?7:8;+dval=(fsref==44100)?5264:1920;+qval=0;+reg=0x8000|qval<<11|pval<<8|jval<<2;+aic26_reg_write(codec,AIC26_REG_PLL_PROG1,reg);+reg=dval<<2;+aic26_reg_write(codec,AIC26_REG_PLL_PROG2,reg);++/* Power up CODEC */+aic26_reg_write(codec,AIC26_REG_POWER_CTRL,0);++/* Audio Control 3 (master mode, fsref rate) */+reg=aic26_reg_read_cache(codec,AIC26_REG_AUDIO_CTRL3);+reg&=~0xf800;+if(aic26->master)+reg|=0x0800;+if(fsref==48000)+reg|=0x2000;+aic26_reg_write(codec,AIC26_REG_AUDIO_CTRL3,reg);++/* Audio Control 1 (FSref divisor) */+reg=aic26_reg_read_cache(codec,AIC26_REG_AUDIO_CTRL1);+reg&=~0x0fff;+reg|=wlen|aic26->datfm|(divisor<<3)|divisor;+aic26_reg_write(codec,AIC26_REG_AUDIO_CTRL1,reg);++return0;+}++/**+*aic26_mute-Mutecontroltoreducenoisewhenchangingaudioformat+*/+staticintaic26_mute(structsnd_soc_codec_dai*dai,intmute)+{+structsnd_soc_codec*codec=dai->codec;+structaic26*aic26=codec->private_data;+u16reg=aic26_reg_read_cache(codec,AIC26_REG_DAC_GAIN);++dev_dbg(&aic26->spi->dev,"aic26_mute(dai=%p, mute=%i)\n",+dai,mute);++if(mute)+reg|=0x8080;+else+reg&=~0x8080;+aic26_reg_write(codec,AIC26_REG_DAC_GAIN,reg);++return0;+}++staticintaic26_set_sysclk(structsnd_soc_codec_dai*codec_dai,+intclk_id,unsignedintfreq,intdir)+{+structsnd_soc_codec*codec=codec_dai->codec;+structaic26*aic26=codec->private_data;++dev_dbg(&aic26->spi->dev,"aic26_set_sysclk(dai=%p, clk_id==%i,"+" freq=%i, dir=%i)\n",+codec_dai,clk_id,freq,dir);++/* MCLK needs to fall between 2MHz and 50 MHz */+if((freq<2000000)||(freq>50000000))+return-EINVAL;++aic26->mclk=freq;+return0;+}++staticintaic26_set_fmt(structsnd_soc_codec_dai*codec_dai,unsignedintfmt)+{+structsnd_soc_codec*codec=codec_dai->codec;+structaic26*aic26=codec->private_data;++dev_dbg(&aic26->spi->dev,"aic26_set_fmt(dai=%p, fmt==%i)\n",+codec_dai,fmt);++/* set master/slave audio interface */+switch(fmt&SND_SOC_DAIFMT_MASTER_MASK){+caseSND_SOC_DAIFMT_CBM_CFM:aic26->master=1;break;+caseSND_SOC_DAIFMT_CBS_CFS:aic26->master=0;break;+default:dev_dbg(&aic26->spi->dev,"bad master\n");return-EINVAL;+}++/* interface format */+switch(fmt&SND_SOC_DAIFMT_FORMAT_MASK){+caseSND_SOC_DAIFMT_I2S:aic26->datfm=AIC26_DATFM_I2S;break;+caseSND_SOC_DAIFMT_DSP_A:aic26->datfm=AIC26_DATFM_DSP;break;+caseSND_SOC_DAIFMT_RIGHT_J:aic26->datfm=AIC26_DATFM_RIGHTJ;break;+caseSND_SOC_DAIFMT_LEFT_J:aic26->datfm=AIC26_DATFM_LEFTJ;break;+default:dev_dbg(&aic26->spi->dev,"bad format\n");return-EINVAL;+}++return0;+}++/* ---------------------------------------------------------------------+*DigitalAudioInterfaceDefinition+*/+structsnd_soc_codec_daiaic26_dai={+.name="tlv320aic26",+.playback={+.stream_name="Playback",+.channels_min=2,+.channels_max=2,+.rates=AIC26_RATES,+.formats=AIC26_FORMATS,+},+.capture={+.stream_name="Capture",+.channels_min=2,+.channels_max=2,+.rates=AIC26_RATES,+.formats=AIC26_FORMATS,+},+.ops={+.hw_params=aic26_hw_params,+},+.dai_ops={+.digital_mute=aic26_mute,+.set_sysclk=aic26_set_sysclk,+.set_fmt=aic26_set_fmt,+},+};+EXPORT_SYMBOL_GPL(aic26_dai);++/* ---------------------------------------------------------------------+*ALSAcontrols+*/+staticconstchar*aic26_capture_src_text[]={"Mic","Aux"};+staticconststructsoc_enumaic26_capture_src_enum=+SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1,12,2,aic26_capture_src_text);++staticconststructsnd_kcontrol_newaic26_snd_controls[]={+/* Output */+SOC_DOUBLE("PCM Playback Volume",AIC26_REG_DAC_GAIN,8,0,0x7f,1),+SOC_DOUBLE("PCM Playback Switch",AIC26_REG_DAC_GAIN,15,7,1,1),+SOC_SINGLE("PCM Capture Volume",AIC26_REG_ADC_GAIN,8,0x7f,0),+SOC_SINGLE("PCM Capture Mute",AIC26_REG_ADC_GAIN,15,1,1),+SOC_SINGLE("Keyclick activate",AIC26_REG_AUDIO_CTRL2,15,0x1,0),+SOC_SINGLE("Keyclick amplitude",AIC26_REG_AUDIO_CTRL2,12,0x7,0),+SOC_SINGLE("Keyclick frequency",AIC26_REG_AUDIO_CTRL2,8,0x7,0),+SOC_SINGLE("Keyclick period",AIC26_REG_AUDIO_CTRL2,4,0xf,0),+SOC_ENUM("Capture Source",aic26_capture_src_enum),+};++/* ---------------------------------------------------------------------+*SoCCODECportionofdriver:probeandreleaseroutines+*/+staticintaic26_probe(structplatform_device*pdev)+{+structsnd_soc_device*socdev=platform_get_drvdata(pdev);+structsnd_soc_codec*codec;+structsnd_kcontrol*kcontrol;+structaic26*aic26;+inti,ret,err;++dev_info(&pdev->dev,"Probing AIC26 SoC CODEC driver\n");+dev_dbg(&pdev->dev,"socdev=%p\n",socdev);+dev_dbg(&pdev->dev,"codec_data=%p\n",socdev->codec_data);++/* Fetch the relevant aic26 private data here (it's already been+*storedinthe.codecpointer)*/+aic26=socdev->codec_data;+if(aic26==NULL){+dev_err(&pdev->dev,"aic26: missing codec pointer\n");+return-ENODEV;+}+codec=&aic26->codec;+socdev->codec=codec;++dev_dbg(&pdev->dev,"Registering PCMs, dev=%p, socdev->dev=%p\n",+&pdev->dev,socdev->dev);+/* register pcms */+ret=snd_soc_new_pcms(socdev,SNDRV_DEFAULT_IDX1,SNDRV_DEFAULT_STR1);+if(ret<0){+dev_err(&pdev->dev,"aic26: failed to create pcms\n");+return-ENODEV;+}++/* register controls */+dev_dbg(&pdev->dev,"Registering controls\n");+for(i=0;i<ARRAY_SIZE(aic26_snd_controls);i++){+kcontrol=snd_soc_cnew(&aic26_snd_controls[i],codec,NULL);+err=snd_ctl_add(codec->card,kcontrol);+WARN_ON(err<0);+}++/* CODEC is setup, we can register the card now */+dev_dbg(&pdev->dev,"Registering card\n");+ret=snd_soc_register_card(socdev);+if(ret<0){+dev_err(&pdev->dev,"aic26: failed to register card\n");+gotocard_err;+}+return0;++card_err:+snd_soc_free_pcms(socdev);+returnret;+}++staticintaic26_remove(structplatform_device*pdev)+{+structsnd_soc_device*socdev=platform_get_drvdata(pdev);+snd_soc_free_pcms(socdev);+return0;+}++structsnd_soc_codec_deviceaic26_soc_codec_dev={+.probe=aic26_probe,+.remove=aic26_remove,+};++/* ---------------------------------------------------------------------+*SPIdeviceportionofdriver:sysfsfilesfordebugging+*/++staticssize_taic26_regs_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structaic26*aic26=dev_get_drvdata(dev);+char*idx=buf;+intcache_flag,addr,page,i,reg;++cache_flag=(strcmp(attr->attr.name,"regs_cache")==0);++for(page=0;page<3;page++){+for(i=0;i<0x20;i++){+addr=AIC26_PAGE_ADDR(page,i);+if(i%8==0)+idx+=sprintf(idx,"%i:%.2i:",page,i);+if(cache_flag)+reg=aic26_reg_read_cache(&aic26->codec,addr);+else+reg=aic26_reg_read(&aic26->codec,addr);+idx+=sprintf(idx," %.4x",reg);+if(i%8==7)+idx+=sprintf(idx,"\n");+}+}+returnidx-buf;+}++staticssize_taic26_keyclick_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structaic26*aic26=dev_get_drvdata(dev);+intval,amp,freq,len;++val=aic26_reg_read_cache(&aic26->codec,AIC26_REG_AUDIO_CTRL2);+amp=(val>>12)&0x7;+freq=(125<<((val>>8)&0x7))>>1;+len=2*(1+((val>>4)&0xf));++returnsprintf(buf,"amp=%x freq=%iHz len=%iclks\n",amp,freq,len);+}++/* Any write to the keyclick attribute will trigger the keyclick */+staticssize_taic26_keyclick_set(structdevice*dev,+structdevice_attribute*attr,+constchar*buf,size_tcount)+{+structaic26*aic26=dev_get_drvdata(dev);+intval;++val=aic26_reg_read_cache(&aic26->codec,AIC26_REG_AUDIO_CTRL2);+val|=0x8000;+aic26_reg_write(&aic26->codec,AIC26_REG_AUDIO_CTRL2,val);++returncount;+}++DEVICE_ATTR(regs,0644,aic26_regs_show,NULL);+DEVICE_ATTR(regs_cache,0644,aic26_regs_show,NULL);+DEVICE_ATTR(keyclick,0644,aic26_keyclick_show,aic26_keyclick_set);++/* ---------------------------------------------------------------------+*SPIdeviceportionofdriver:probeandreleaseroutinesandSPI+*driverregistration.+*/+staticintaic26_spi_probe(structspi_device*spi)+{+structaic26*aic26;+intrc,i,reg;++dev_dbg(&spi->dev,"probing tlv320aic26 spi device\n");++/* Allocate driver data */+aic26=kzalloc(sizeof*aic26,GFP_KERNEL);+if(!aic26)+return-ENOMEM;++/* Initialize the driver data */+aic26->spi=spi;+dev_set_drvdata(&spi->dev,aic26);++/* Setup what we can in the codec structure so that the register+*accessfunctionswillworkasexpected.Morewillbefilled+*outwhenitisprobedbytheSoCCODECpartofthisdriver*/+aic26->codec.private_data=aic26;+aic26->codec.name="aic26";+aic26->codec.owner=THIS_MODULE;+aic26->codec.dai=&aic26_dai;+aic26->codec.num_dai=1;+aic26->codec.read=aic26_reg_read;+aic26->codec.write=aic26_reg_write;+aic26->master=1;+mutex_init(&aic26->codec.mutex);+INIT_LIST_HEAD(&aic26->codec.dapm_widgets);+INIT_LIST_HEAD(&aic26->codec.dapm_paths);+aic26->codec.reg_cache_size=sizeof(aic26->reg_cache);+aic26->codec.reg_cache=aic26->reg_cache;++/* Reset the codec to power on defaults */+aic26_reg_write(&aic26->codec,AIC26_REG_RESET,0xBB00);++/* Power up CODEC */+aic26_reg_write(&aic26->codec,AIC26_REG_POWER_CTRL,0);++/* Audio Control 3 (master mode, fsref rate) */+reg=aic26_reg_read(&aic26->codec,AIC26_REG_AUDIO_CTRL3);+reg&=~0xf800;+reg|=0x0800;/* set master mode */+aic26_reg_write(&aic26->codec,AIC26_REG_AUDIO_CTRL3,reg);++/* Fill page 2 register cache */+for(i=0;i<ARRAY_SIZE(aic26->reg_cache);i++)+aic26_reg_read(&aic26->codec,AIC26_PAGE_ADDR(2,i));++/* Register the sysfs files for debugging */+/* Create SysFS files */+rc=device_create_file(&spi->dev,&dev_attr_regs);+rc|=device_create_file(&spi->dev,&dev_attr_regs_cache);+rc|=device_create_file(&spi->dev,&dev_attr_keyclick);+if(rc)+dev_info(&spi->dev,"error creating sysfs files\n");++#if defined(CONFIG_SND_SOC_OF)+/* Tell the of_soc helper about this codec */+of_snd_soc_register_codec(&aic26_soc_codec_dev,aic26,&aic26_dai,+spi->dev.archdata.of_node);+#endif++dev_dbg(&spi->dev,"SPI device initialized\n");+return0;+}++staticintaic26_spi_remove(structspi_device*spi)+{+structaic26*aic26=dev_get_drvdata(&spi->dev);++kfree(aic26);++return0;+}++staticstructspi_driveraic26_spi={+.driver={+.name="tlv320aic26",+.owner=THIS_MODULE,+},+.probe=aic26_spi_probe,+.remove=aic26_spi_remove,+};++staticint__initaic26_init(void)+{+returnspi_register_driver(&aic26_spi);+}+module_init(aic26_init);++staticvoid__exitaic26_exit(void)+{+spi_unregister_driver(&aic26_spi);+}+module_exit(aic26_exit);
From: Mark Brown <hidden> Date: 2008-07-15 10:13:19
On Mon, Jul 14, 2008 at 07:45:46PM -0400, Jon Smirl wrote:
On 7/14/08, Timur Tabi [off-list ref] wrote:
quoted
Mark Brown wrote:
quoted
> chassis - on Linux drivers can be automatically loaded based on these
> strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
> that does this.
Allowing multiple binds at the root causes the problem of something
like compatible="lite5200b,mpc5200-simple". Both platforms would bind
and that's not what you want.
Binding isn't the issue here - it's loading the driver in the first
place. Once the drivers are loaded they can (hopefully) figure out if
they are running on appropriate hardware.
Another scheme would be to add kernel code to always create virtual OF
devices like "lite5200b-fabric" that are derived off from the machine
name that achieved a bind.
This is what I'm suggesting, modulo the fact that I'm suggesting *not*
creating virtual devices but rather providing a mechanism for drivers to
load without binding to anything. It strikes me that you're going to
run into similar situations with other hardware at some point - either
for undocumented extras that you happen to know exist on the system
(like much of the DMI usage on x86) or for other things where you've got
on-board hardware structured like sound hardware tends to be.
From: Mark Brown <hidden> Date: 2008-07-15 10:33:41
On Tue, Jul 15, 2008 at 01:27:52PM +0530, dinesh wrote:
I have one query about this soc driver.
I want to know when u will merge it with kernel then
where and by which name this device will be available
in /dev directory of your file system.
As i am following the same structure for my driver so please help me. I
am ot able to recognise the device in the file system.
It will appear via the standard ALSA interfaces (and OSS interfaces if
you enable OSS compatibility). The standard location for ALSA device
files is under /dev/snd where you'll see several files per device.
thanks for your response but there is no /dev/snd directory in my file
structure is there any special method to create it please tell in
detail.
-----Original Message-----
From: Mark Brown <redacted>
To: dinesh <redacted>
Cc: Grant Likely <redacted>, linuxppc-dev@ozlabs.org,
alsa-devel@alsa-project.org, liam.girdwood@wolfsonmicro.com
Subject: Re: [alsa-devel] [PATCH v2 3/3] ALSA SoC: Add Texas Instruments
TLV320AIC26 codec driver
Date: Tue, 15 Jul 2008 11:33:34 +0100
On Tue, Jul 15, 2008 at 01:27:52PM +0530, dinesh wrote:
I have one query about this soc driver.
I want to know when u will merge it with kernel then
where and by which name this device will be available
in /dev directory of your file system.
As i am following the same structure for my driver so please help me. I
am ot able to recognise the device in the file system.
It will appear via the standard ALSA interfaces (and OSS interfaces if
you enable OSS compatibility). The standard location for ALSA device
files is under /dev/snd where you'll see several files per device.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
From: Mark Brown <hidden> Date: 2008-07-15 12:46:50
On Tue, Jul 15, 2008 at 06:08:19PM +0530, dinesh wrote:
thanks for your response but there is no /dev/snd directory in my file
structure is there any special method to create it please tell in
detail.
The devices appear via the standard kernel interfaces so if you are
using udev or mdev they should be created automatically. Otherwise
you'll need to create them statically using whatever method you usually
use (eg, a MAKEDEV script supplied by your distribution or manually by
reference to the device numbers exposed in /sys/class/sound/*/dev).
The ALSA devices use the standard Linux mechanisms to create their
devices so whatever you normally use to create devices should work for
ALSA too.
From: Jon Smirl <hidden> Date: 2008-07-15 13:08:29
On 7/15/08, Mark Brown [off-list ref] wrote:
On Mon, Jul 14, 2008 at 07:45:46PM -0400, Jon Smirl wrote:
> On 7/14/08, Timur Tabi [off-list ref] wrote:
> > Mark Brown wrote:
quoted
quoted
> chassis - on Linux drivers can be automatically loaded based on these
> > > strings. See drivers/misc/thinkpad_acpi.c for an example of a driver
> > > that does this.
quoted
Allowing multiple binds at the root causes the problem of something
> like compatible="lite5200b,mpc5200-simple". Both platforms would bind
> and that's not what you want.
Binding isn't the issue here - it's loading the driver in the first
place. Once the drivers are loaded they can (hopefully) figure out if
they are running on appropriate hardware.
In this case we would end up with two core PowerPC machine drivers
bound, not ALSA ones. We have machine drivers in the PowerPC core,
that's one reason why it is confusing to call the ALSA drivers machine
drivers too.
If we allow multiple bindings both lite5200b and mpc5200-simple would
bind. The compatible list is a priority list from most specific to
most general. First you check for the specific driver lite5200b, if
it's not found then load the generic one mpc5200-simple.
> Another scheme would be to add kernel code to always create virtual OF
> devices like "lite5200b-fabric" that are derived off from the machine
> name that achieved a bind.
This is what I'm suggesting, modulo the fact that I'm suggesting *not*
creating virtual devices but rather providing a mechanism for drivers to
load without binding to anything. It strikes me that you're going to
If you have drivers for four different hardware releases compiled into
your kernel, the only kernel mechanism I know of to trigger only one
of these to initialize is to create a device and then let the probe
mechanism sort it out. This is even more true when the drivers are on
initrd and need to be dynamically loaded. The module load code will
search through the alias lists in the module .ko files.
It has been pointed out that a lite5200b-fabric device isn't really a
virtual devices. It's a device that represents the traces on the PCB
wiring the generic chip drivers together.
run into similar situations with other hardware at some point - either
for undocumented extras that you happen to know exist on the system
(like much of the DMI usage on x86) or for other things where you've got
on-board hardware structured like sound hardware tends to be.
This makes sense, it is possible that other areas we aren't familiar
with will need fabric drivers too. The problem is easily exposed in
audio hardware since we use external clock and amp chips.
--
Jon Smirl
jonsmirl@gmail.com
From: Mark Brown <hidden> Date: 2008-07-15 14:04:42
On Tue, Jul 15, 2008 at 09:08:28AM -0400, Jon Smirl wrote:
On 7/15/08, Mark Brown [off-list ref] wrote:
quoted
Binding isn't the issue here - it's loading the driver in the first
place. Once the drivers are loaded they can (hopefully) figure out if
they are running on appropriate hardware.
In this case we would end up with two core PowerPC machine drivers
bound, not ALSA ones. We have machine drivers in the PowerPC core,
that's one reason why it is confusing to call the ALSA drivers machine
drivers too.
Again, I'm not talking about binding anything.
quoted
This is what I'm suggesting, modulo the fact that I'm suggesting *not*
creating virtual devices but rather providing a mechanism for drivers to
load without binding to anything. It strikes me that you're going to
If you have drivers for four different hardware releases compiled into
your kernel, the only kernel mechanism I know of to trigger only one
of these to initialize is to create a device and then let the probe
mechanism sort it out. This is even more true when the drivers are on
initrd and need to be dynamically loaded. The module load code will
search through the alias lists in the module .ko files.
This can be handled in the module initialisation (rather than driver
probe) - providing something can arrange for the driver to get loaded
then the drivers can check the system they're running on when that
happens and fail if it's inappropriate. The ARM ASoC drivers and x86
DMI based stuff do things this way, for example (ARM doesn't have the
automatic module load stuff implemented, though).
The infrastructure for automatically loading modules is usable
separately from device registration, though normally the two do go hand
in hand.
It has been pointed out that a lite5200b-fabric device isn't really a
virtual devices. It's a device that represents the traces on the PCB
wiring the generic chip drivers together.
This was brought up very early on when you guys first started working on
it. :/
Hi,
I am writing a soc sound driver for MPC8323 board linux 2.6.24 in which i want to do data transfer to and from device myself using BUFFER DESCRIPTOR not with the usual DMA transfer.
Please help me.
Hi dinesh,
If you are working on ARM, see the ARM AACi code, in sound/arm/aaci.c
Thanks
Nobin Mathew.
On 7/16/08, dinesh [off-list ref] wrote:
Hi,
I am writing a soc sound driver for MPC8323 board linux 2.6.24 in which i
want to do data transfer to and from device myself using BUFFER DESCRIPTOR
not with the usual DMA transfer.
Please help me.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
no i am working on powerpc. MPC8323
-----Original Message-----
From: Nobin Mathew <redacted>
To: dinesh <redacted>
Cc: Grant Likely <redacted>, linuxppc-dev@ozlabs.org,
alsa-devel@alsa-project.org, liam.girdwood@wolfsonmicro.com
Subject: Re: [alsa-devel] WRITING AN SOC DRIVER WITHOUT DMA
Date: Wed, 16 Jul 2008 15:37:54 +0530
Hi dinesh,
If you are working on ARM, see the ARM AACi code, in sound/arm/aaci.c
Thanks
Nobin Mathew.
On 7/16/08, dinesh [off-list ref] wrote:
Hi,
I am writing a soc sound driver for MPC8323 board linux 2.6.24 in which i
want to do data transfer to and from device myself using BUFFER DESCRIPTOR
not with the usual DMA transfer.
Please help me.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Hi,
I think no one is able to understand my problem, let me break it to low
level.
What i want is that i have a buffer in driver code which is also handled
by some other application i want that this buffer data is to be used for
capture and playback stream fills data to another buffer which i can
passover to my other application.
If someone can help me please.
Regards,
Dinesh
-----Original Message-----
From: dinesh <redacted>
To: Nobin Mathew <redacted>
Cc: Grant Likely <redacted>, linuxppc-dev@ozlabs.org,
alsa-devel@alsa-project.org, liam.girdwood@wolfsonmicro.com
Subject: Re: [alsa-devel] WRITING AN SOC DRIVER WITHOUT DMA
Date: Wed, 16 Jul 2008 15:43:26 +0530
no i am working on powerpc. MPC8323
-----Original Message-----
From: Nobin Mathew <redacted>
To: dinesh <redacted>
Cc: Grant Likely <redacted>, linuxppc-dev@ozlabs.org,
alsa-devel@alsa-project.org, liam.girdwood@wolfsonmicro.com
Subject: Re: [alsa-devel] WRITING AN SOC DRIVER WITHOUT DMA
Date: Wed, 16 Jul 2008 15:37:54 +0530
Hi dinesh,
If you are working on ARM, see the ARM AACi code, in sound/arm/aaci.c
Thanks
Nobin Mathew.
On 7/16/08, dinesh [off-list ref] wrote:
Hi,
I am writing a soc sound driver for MPC8323 board linux 2.6.24 in which i
want to do data transfer to and from device myself using BUFFER DESCRIPTOR
not with the usual DMA transfer.
Please help me.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
From: Mark Brown <hidden> Date: 2008-07-17 10:56:24
On Thu, Jul 17, 2008 at 11:33:31AM +0530, dinesh wrote:
What i want is that i have a buffer in driver code which is also handled
by some other application i want that this buffer data is to be used for
capture and playback stream fills data to another buffer which i can
passover to my other application.
Depending on what exactly you're doing here you may find that this is
best implemented in user space with an ALSA plugin rather than doing it
as part of a driver. If you do want to do this in kernel space then the
parts of an ASoC driver which transfer audio data are just the same as
those for any other ALSA driver so things like sound/arm/aaci.c may
provide useful examples.
Hi Dinesh,
If that is your requirement then go and see the actual code
sound/arm/aaci.c. They are not using DMA. It is programmed IO.
Thanks
Nobin Mathew
On 7/17/08, Mark Brown [off-list ref] wrote:
On Thu, Jul 17, 2008 at 11:33:31AM +0530, dinesh wrote:
quoted
What i want is that i have a buffer in driver code which is also handled
by some other application i want that this buffer data is to be used for
capture and playback stream fills data to another buffer which i can
passover to my other application.
Depending on what exactly you're doing here you may find that this is
best implemented in user space with an ALSA plugin rather than doing it
as part of a driver. If you do want to do this in kernel space then the
parts of an ASoC driver which transfer audio data are just the same as
those for any other ALSA driver so things like sound/arm/aaci.c may
provide useful examples.
From: Jon Smirl <hidden> Date: 2008-07-17 12:05:51
On 7/17/08, Nobin Mathew [off-list ref] wrote:
Hi Dinesh,
If that is your requirement then go and see the actual code
sound/arm/aaci.c. They are not using DMA. It is programmed IO.
Search around the list archives, the first version of the
Efika/mpc5200 ac97 driver was programmed IO. The next version updated
it to DMA.
Thanks
Nobin Mathew
On 7/17/08, Mark Brown
[off-list ref] wrote:
quoted
On Thu, Jul 17, 2008 at 11:33:31AM +0530, dinesh wrote:
quoted
What i want is that i have a buffer in driver code which is also handled
by some other application i want that this buffer data is to be used for
capture and playback stream fills data to another buffer which i can
passover to my other application.
Depending on what exactly you're doing here you may find that this is
best implemented in user space with an ALSA plugin rather than doing it
as part of a driver. If you do want to do this in kernel space then the
parts of an ASoC driver which transfer audio data are just the same as
those for any other ALSA driver so things like sound/arm/aaci.c may
provide useful examples.
I believe ASoC creates sysfs entries if you use reg_cache and reg_cache_size, so
you should not be creating your own sysfs entries if you use these variables.
+
+#if defined(CONFIG_SND_SOC_OF)
+ /* Tell the of_soc helper about this codec */
+ of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
+ spi->dev.archdata.of_node);
+#endif
I haven't paid any attention to the of_soc helper, because it's for ASoC V1
only. However, I don't understand why you need to reference it in the codec
driver. My CS4270 codec driver knows nothing about OF and works fine in
arch/powerpc.
--
Timur Tabi
Linux kernel developer at Freescale
From: Grant Likely <hidden> Date: 2008-07-18 06:29:41
On Mon, Jul 14, 2008 at 12:45:55PM +0100, Mark Brown wrote:
On Sat, Jul 12, 2008 at 02:39:39AM -0600, Grant Likely wrote:
quoted
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
This looks basically good - most of the issues below are nitpicky.
Does the PLL configuration not depend on the input clock frequency? You
have a set_sysclk() method to configure the MCLK supplied but the driver
never appears to reference the value anywhere.
Yes, this should be done better. I'm working on making this better, but
I'm hoping it is okay to have that as follow-on patches.
quoted
+ /* Power up CODEC */
+ aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
As discussed this should probably just be removed from hw_params().
done
quoted
+static int aic26_set_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt)
+{
quoted
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break;
+ case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break;
+ case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
+ case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break;
+ default: dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
I'm having a hard time liking this indentation style. It's not an
obstacle to merging but it doesn't really help legibility - there's not
enough white space and once you have a non-standard line like the
default: one.
checkpatch complains about the 12,2 here and a bunch of other stuff -
ALSA is generally very strict about that.
Originally, that had been to keep the line under 80 chars, but some of
the names have changed since then to make it no longer necessary. I'll
clean up the checkpatch stuff.
This configuration is also exposed via a sysfs file, including some of
the configurability. Exposing the information via sysfs isn't a
particular problem but allowing it to be written to without going
through ALSA seems wrong.
All the written bit does is trigger the keyclick event. It doesn't
adjust the parameters in any way.
From: Grant Likely <hidden> Date: 2008-07-18 07:17:24
On Mon, Jul 14, 2008 at 06:16:33PM +0100, Mark Brown wrote:
On Mon, Jul 14, 2008 at 11:06:34AM -0600, Grant Likely wrote:
quoted
I'm okay with that. How about fsl/mpc5200-of-machine.c for now?
(only the mpc5200 i2s driver uses it at the moment). It can always be
renamed if other folks want to use it for other chips.
That seems reasonable so long as you're happy with it, though it does
seem to be going to the other extreme in terms of broadness :)
Okay, I've changed my mind. :-) I'll back off a bit from this extreme and
call it:
sound/soc/fsl/soc-of-simple.c
Does that sound okay? If non-freescale chips decide to use it then it
can be moved out of the freescale director.
Cheers,
g.
From: Mark Brown <hidden> Date: 2008-07-18 10:39:52
On Fri, Jul 18, 2008 at 12:29:37AM -0600, Grant Likely wrote:
On Mon, Jul 14, 2008 at 12:45:55PM +0100, Mark Brown wrote:
quoted
On Sat, Jul 12, 2008 at 02:39:39AM -0600, Grant Likely wrote:
quoted
This configuration is also exposed via a sysfs file, including some of
the configurability. Exposing the information via sysfs isn't a
particular problem but allowing it to be written to without going
through ALSA seems wrong.
All the written bit does is trigger the keyclick event. It doesn't
adjust the parameters in any way.
That should be OK for now but we ought to work out a way of doing this
via an ALSA control so it can be standardised over the devices that
support it. I can't see anything suitable currently, though.
quoted
quoted
+#if defined(CONFIG_SND_SOC_OF)
+ /* Tell the of_soc helper about this codec */
+ of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
+ spi->dev.archdata.of_node);
+#endif
quoted
CONFIG_SND_SOC_OF could be a module - this needs to also check for it
being a module.
Doesn't #if defined() match on both static and module selection?