本文是基於mini2440開發板Linux版本號是linux-2.6.32.2的學習筆記
一. uda1341的硬件信息
CPU通過L3接口控制uda1341芯片,分別是:
L3DATA: 數據線
L3MODE: 模式控制線,置0時,地址模式;置1時,數據模式
L3CLOCK: 時鐘線
- 地址模式:
data[7:2]:表示的是設備地址,UDA1341TS芯片的設備地址是 000101
data[1:0]:表示的是傳輸類型
00:地址寄存器,音量,低音增強,高音,峯值檢測
擴展寄存器地址,AGC控制,MIC靈敏度控制等
01:讀回峯值信息
10: STATUS狀態信息,復位、時鐘、數據輸入格式(數據位寬)等
STATUS控制,地址是:00010110 = 0x16
data0控制:地址是:00010100 = 0x14
data1控制,地址是:00010101 = 0x15
三. uda1341的寫控制函數
寫控制函數定義如下:
static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
- 將value保存在cache[reg]中。
uda134x_write_reg_cache(codec, reg, value);
- 根據傳入的reg值,寫地址。
- UDA134X_EA000 ~ UDA134X_EA111屬於擴展地址,也屬於data[0:0]
發送地址是:(5 << 2) | 0 ),發送地址時,L3MODE引腳拉低,地址模式。
發送擴展地址,擴展地址最多有8個,但是擴展地址的最高兩位爲1,所以地址要與上0xc0
#define UDA134X_EXTADDR_PREFIX 0xC0
addr = (reg | UDA134X_EXTADDR_PREFIX);
ret = l3_write(&pd->l3, UDA134X_DATA0_ADDR, &addr, 1);
發送擴展數據,擴展數據的高三位都是1,所以要在原來value上加0xe0
#define UDA134X_EXTDATA_PREFIX 0xE0
data = (value | UDA134X_EXTDATA_PREFIX);
ret = l3_write(&pd->l3, addr, &data, 1);
發送擴展地址和發送擴展數據,實際上都是發送數據,L3MODE引腳要拉高,選擇數據模式。
- UDA134X_STATUS0和UDA134X_STATUS1屬於data[1:0]
發送地址是:(5 << 2) | 2 )
發送數據:
#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2)
addr = UDA134X_STATUS_ADDR;
ret = l3_write(&pd->l3, addr, &data, 1);
- UDA134X_DATA000,UDA134X_DATA001,UDA134X_DATA010屬於data[0:0]
發送地址是:(5 << 2) | 0 )
發送數據:
#define UDA134X_L3ADDR 5
#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0)
addr = UDA134X_DATA0_ADDR;
ret = l3_write(&pd->l3, addr, &data, 1);
- UDA134X_DATA1屬於data[0:1]
發送地址是:(5 << 2) | 1 )
發送數據:
#define UDA134X_L3ADDR 5
#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1)
addr = UDA134X_DATA1_ADDR;
ret = l3_write(&pd->l3, addr, &data, 1);
四. uda1341的復位操作
發送地址:(5 << 2) | 2 )
發送數據:value | (1<<6)
五. uda1341的靜音操作
發送地址:(5 << 2) | 0 )
發送數據:value | (1<<2)
六. uda1341的時鐘設置
1.設置系統時鐘
uda1341支持三種系統時鐘, 512fs, 384fs,256fs
2.uda1341支持的採樣率爲:8000,11025,16000,22050,32000,44100,48000
3.則系統時鐘的範圍是:256 * 8000 <= freq <= 512 * 48000
2.設置時鐘調用函數uda134x_set_dai_sysclk
static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct uda134x_priv *uda134x = codec->private_data;
pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
clk_id, freq, dir);
/* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
because the codec is slave. Of course limitations of the clock
master (the IIS controller) apply.
We'll error out on set_hw_params if it's not OK */
if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
uda134x->sysclk = freq;
return 0;
}
printk(KERN_ERR "%s unsupported sysclk\n", __func__);
return -EINVAL;
}
在這裏系統時鐘並沒有真正的設置下去,而是保存到了uda134x->sysclk變量中,留在後面設置。
七. 設置支持數據格式
1.uda1341支持的格式如下:
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
- 設置數據格式調用的函數是uda134x_set_dai_fmt
static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct uda134x_priv *uda134x = codec->private_data;
pr_debug("%s fmt: %08X\n", __func__, fmt);
/* codec supports only full slave mode */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
printk(KERN_ERR "%s unsupported slave mode\n", __func__);
return -EINVAL;
}
/* no support for clock inversion */
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
return -EINVAL;
}
/* We can't setup DAI format here as it depends on the word bit num */
/* so let's just store the value for later */
uda134x->dai_fmt = fmt;
return 0;
}
八. uda1341設置bias(猜測是電源)等級
SND_SOC_BIAS_ON等級:
把ADC, DAC打開
發送地址:(5 << 2) | 2 )
發送數據:0x83
SND_SOC_BIAS_PREPARE等級:
pd->power(1);
for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
codec->write(codec, i, *cache++);
SND_SOC_BIAS_STANDBY等級:
發送地址:(5 << 2) | 2 )
發送數據:value & ~(0x03)
SND_SOC_BIAS_OFF等級:
pd->power(0);
九. 設置硬件參數
設置硬件參數調用的函數是uda134x_hw_params
- 設置芯片的系統時鐘的除數,512, 384,256
switch (uda134x->sysclk / params_rate(params))
{
case 512:
break;
case 384:
hw_params |= (1<<4);
break;
case 256:
hw_params |= (1<<5);
break;
default:
printk(KERN_ERR "%s unsupported fs\n", __func__);
return -EINVAL;
}
發送數據:384fs(vaule | (1 << 4)) 256fs(value | 1 << 5)
- 設置數據模式
switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK)
{
case SND_SOC_DAIFMT_I2S:
break;
case SND_SOC_DAIFMT_RIGHT_J:
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
hw_params |= (1<<1);
break;
case SNDRV_PCM_FORMAT_S18_3LE:
hw_params |= (1<<2);
break;
case SNDRV_PCM_FORMAT_S20_3LE:
hw_params |= ((1<<2) | (1<<1));
break;
default:
printk(KERN_ERR "%s unsupported format (right)\n",
__func__);
return -EINVAL;
}
break;
case SND_SOC_DAIFMT_LEFT_J:
hw_params |= (1<<3);
break;
default:
printk(KERN_ERR "%s unsupported format\n", __func__);
return -EINVAL;
}
SND_SOC_DAIFMT_I2S:0
SND_SOC_DAIFMT_RIGHT_J和SND_SOC_DAIFMT_LEFT_J模式參考下圖
十. Linux UDA1341芯片接口註冊
static struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
.hw_params = uda134x_hw_params,
.digital_mute = uda134x_mute,
.set_sysclk = uda134x_set_dai_sysclk,
.set_fmt = uda134x_set_dai_fmt,
};
struct snd_soc_dai uda134x_dai = {
.name = "UDA134X",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = &uda134x_dai_ops,
};
- 註冊時間:在啓動內核是調用uda134x_init函數。在uda134x_init函數中註冊UDA1341接口。
static int __init uda134x_init(void)
{
return snd_soc_register_dai(&uda134x_dai);
}
module_init(uda134x_init);
跟I2S的接口註冊一樣,註冊到一個dai_list的全局鏈表中,dai_list鏈表中是各種各樣的dai接口。
十一. 總結
- uda134x_dai接口在內核起來時註冊。
- uda134x_dai接口包含對UDA1341芯片的時鐘,傳輸模式等硬件設置,一個硬件參數操作集uda134x_dai_ops。
參考博客:
https://blog.csdn.net/gqb_driver/article/details/8551551