查看datasheet可以知道,mt7620和rt5350有相通的Audio组件
从openwrt14.07的kernel源码中,我们可以找到mt7620 i2s的驱动
我们的pcm驱动,可以基于i2s驱动进行改动。pcm驱动已传至github,相关代码可以看这里
主要改动了以下几点:
参考datasheet,定义PCM相关的寄存器,把i2s驱动里对应的寄存器操作换成对PCM部分寄存器的操作
#define PCM_GLB_CFG 0x00
#define PCM_GLB_CFG_EN BIT(31)
#define PCM_GLB_CFG_DMA_EN BIT(30)
#define PCM_GLB_CFG_CH0_TX_EN BIT(8)
#define PCM_GLB_CFG_CH0_RX_EN BIT(0)
#define PCM_GLB_CFG_RFF_THRES 20
#define PCM_GLB_CFG_TFF_THRES 16
#define PCM_GLB_CFG_DFT_THRES (4 << PCM_GLB_CFG_RFF_THRES) | \
(4 << PCM_GLB_CFG_TFF_THRES)
#define PCM_PCM_CFG 0x04
#define PCM_PCM_CFG_CLKOUT_EN BIT(30)
#define PCM_PCM_CFG_EXT_FSYNC BIT(27)
#define PCM_PCM_CFG_LONG_FSYNC BIT(26)
#define PCM_PCM_CFG_FSYNC_POL BIT(25)
#define PCM_INT_STATUS 0x08
#define PCM_INT_EN 0x0C
#define PCM_FF_STATUS 0x10
#define PCM_CH0_CFG 0x20
#define PCM_CH1_CFG 0x24
#define PCM_FSYNC_CFG 0x30
#define PCM_CH_CFG2 0x34
#define PCM_DIVCOMP_CFG 0x50
#define PCM_DIVCOMP_CFG_CLK_EN BIT(31)
#define PCM_DIVINT_CFG 0x54
#define PCM_DIGDELAY_CFG 0x60
#define PCM_CH0_FIFO 0x80
#define PCM_CH1_FIFO 0x84
参考RT5350 Preliminary Datasheet.pdf第3.12.3节
Peripheral Channel Connection部分可知,
PCM Channel0 对应的GDMA Slot为4(RX)、6(TX),
在配置dma channel的时候做对应的改动即可
static void rt5350_init_pcm_config(struct rt5350_pcm *pcm)
{
struct snd_dmaengine_dai_dma_data *dma_data;
/* Playback */
dma_data = &pcm->playback_dma_data;
dma_data->maxburst = 16;
dma_data->slave_id = 6;
dma_data->addr = pcm->phys_base + PCM_CH0_FIFO; //only use channel 0
/* Capture */
dma_data = &pcm->capture_dma_data;
dma_data->maxburst = 16;
dma_data->slave_id = 4;
dma_data->addr = pcm->phys_base + PCM_CH0_FIFO;
}
以rt5350做pcm slave,sim800模块做master为例
sim800模块的pcm参数如下:
a) PCM clock 为256kHz
b) PCM sync 为 short sync,极性为高有效
于是参考datasheet对PCM_CFG寄存器的描述,可以得到下面改动
rt5350_pcm_dai_probe
///////////// pcm general config
cfg = rt5350_pcm_read(pcm, PCM_FSYNC_CFG);
cfg &= ~PCM_PCM_CFG_LONG_FSYNC; //short sync mode
cfg |= PCM_PCM_CFG_FSYNC_POL; // sync high active
//slot mode, pcm clock = 256KHz
cfg &= ~(0x07);
cfg = 0; // 4 slots
rt5350_pcm_write(pcm, PCM_PCM_CFG, cfg);
///////////// pcm sync config
cfg = rt5350_pcm_read(pcm, PCM_FSYNC_CFG);
// pol, etc.
rt5350_pcm_write(pcm, PCM_FSYNC_CFG, cfg);
//When using the external clock, the frequency clock
//should be equal to the PCM_clock out. Otherwise, the
//PCM_CLKin should be 8.192 MHz.
rt5350_pcm_write(pcm, PCM_DIVINT_CFG, i2sMaster_inclk_int[PCMCLOCK_OUT]);
rt5350_pcm_write(pcm, PCM_DIVCOMP_CFG, i2sMaster_inclk_comp[PCMCLOCK_OUT] | PCM_DIVCOMP_CFG_CLK_EN);
----------------------------
rt5350_pcm_set_fmt
cfg = rt5350_pcm_read(pcm, PCM_PCM_CFG);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
cfg &= ~PCM_PCM_CFG_CLKOUT_EN; // pcm clock from external
cfg |= PCM_PCM_CFG_EXT_FSYNC; // pcm sync from external
break;
case SND_SOC_DAIFMT_CBM_CFM:
cfg |= PCM_PCM_CFG_CLKOUT_EN; // pcm clock from internal
cfg &= ~PCM_PCM_CFG_EXT_FSYNC; // pcm sync from internal
break;
case SND_SOC_DAIFMT_CBM_CFS:
default:
return -EINVAL;
}
rt5350_pcm_write(pcm, PCM_PCM_CFG, cfg);
因为rt5350/mt7620上PCM是复用脚,所以需要配置成PCM功能,
这部分通过修改dts文件即可,
另外,PCM驱动设备节点的声明,codec machine层、codec层驱动设备节点的声明
也都是在dts文件里完成,
具体如下
rt5350.dtsi
pcm: pcm@2000 {
compatible = "ralink,rt5350-pcm";
reg = <0x2000 0x800>;
resets = <&rstctrl 11>;
reset-names = "pcm";
interrupt-parent = <&intc>;
interrupts = <4>;
dmas = <&gdma 4>,
<&gdma 5>;
dma-names = "tx", "rx";
status = "disabled";
};
SXX.dts
//使能pcm驱动
palmbus@10000000 {
pcm@2000 {
status = "okay";
};
}
//管脚功能初始化
pinctrl {
state_default: pinctrl0 {
gpio {
ralink,group = "i2c", "jtag";
ralink,function = "gpio";
};
uartf {
ralink,group = "uartf";
ralink,function = "pcm uartf";
};
};
};
//声明codec设备节点
codec: sxx-pcm-codec {
compatible = "ralink,sxx-pcm-codec";
};
//声明codec machine层设备节点
sound: sxx-pcm-machine {
compatible = "ralink,sxx-pcm-machine";
model = "sxx-pcm-machine";
cpu-dai = <&pcm>;
codec-dai = <&codec>;
};
来源:http://transing.xyz/2015/08/19/openwrt-pcm-driver-on-mt7620-or-rt5350/