ASoC是建立在标准ALSA驱动层上 ,对底层的alsa框架封装了一层,为了更好的支持嵌入式cpu和音频解码器设备的一套软件体系
在ASOC出现之前 解码器驱动和平台CPU驱动联系过于紧密,导致不同平台的解码器驱动代码的重复,耳机麦克风的热插拔都需要根据不同平台来进行修改等,ASOC就是为了解决这些问题才被提出来的。
一般指某个平台比如s3c2440,imx6ull等,一般包括了DMA,I2S,PCM等接口,使得一款CPU封装好Platform驱动 就可以在不同的机器上进行使用。一般包括了这个CPU的音频DMA和音频接口控制和配置等,与解码器无关。
解码器驱动,例如解码器WM8960 里面包含了I2S(一般用来修改寄存器),I2C 多个输入多个输出等接口,codec驱动使得同一款解码器在不同机器上变得可重用。codec驱动的设计与平台本身无关,一般包含了一些音频控件,音频接口,动态音频电源管理,IO等功能。
机器的驱动,每个不同的机器对应一个不同的Machine驱动,Machine驱动是为了绑定Platform驱动和Codec 驱动来实现的
struct snd_soc_dai_ops {
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);//设置主时钟
int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);//设置PLL参数
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);//设置分频系数
int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);
/*
* DAI format configuration
* Called by soc_card drivers, normally in their hw_params.
*/
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);//设置dai的格式
int (*xlate_tdm_slot_mask)(unsigned int slots,
unsigned int *tx_mask, unsigned int *rx_mask);
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);//如果dai支持时分复用,用于设置时分复用的slot
int (*set_channel_map)(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot);//声道的时分复用映射设置
int (*get_channel_map)(struct snd_soc_dai *dai,
unsigned int *tx_num, unsigned int *tx_slot,
unsigned int *rx_num, unsigned int *rx_slot);//获取声道的时分复用映射的配置
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);//设置dai引脚的状态,当与其他dai并联使用同一引脚时需要使用该回调
int (*set_sdw_stream)(struct snd_soc_dai *dai,
void *stream, int direction);
/*
* DAI digital mute - optional.
* Called by soc-core to minimise any pops.
*/
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);
int (*startup)(struct snd_pcm_substream *,
struct snd_soc_dai *);//当应用程序打开一个pcm设备时,该函数会被调用
void (*shutdown)(struct snd_pcm_substream *,
struct snd_soc_dai *);//当应用程序关闭一个pcm设备时,该函数会被调用
int (*hw_params)(struct snd_pcm_substream *,
struct snd_pcm_hw_params *, struct snd_soc_dai *);//驱动的hw_params阶段,该函数会被调用
int (*hw_free)(struct snd_pcm_substream *,
struct snd_soc_dai *);
int (*prepare)(struct snd_pcm_substream *,
struct snd_soc_dai *);//正式开始数据传送之前会调用该函数
int (*trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);//数据传送的开始,暂停,恢复和停止时,该函数会被调用
int (*bespoke_trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);
/*
* For hardware based FIFO caused delay reporting.
* Optional.
*/
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
struct snd_soc_dai *);
};
machine驱动调用下面的函数,machine驱动在他的snd_pcm_ops字段中的hw_params回调中使用这些
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div)
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
/*
fmt :
[0 : 3] 用于设置接口的格式
SND_SOC_DAIFMT_I2S 1 /* I2S mode
SND_SOC_DAIFMT_RIGHT_J 2 /* Right Justified mode
SND_SOC_DAIFMT_LEFT_J 3 /* Left Justified mode
SND_SOC_DAIFMT_DSP_A 4 /* L data MSB after FRM LRC
SND_SOC_DAIFMT_DSP_B 5 /* L data MSB during FRM LRC
SND_SOC_DAIFMT_AC97 6 /* AC97
SND_SOC_DAIFMT_PDM 7 /* Pulse density modulation
[4 : 7] 用于设置接口的格式
SND_SOC_DAIFMT_CONT (1 << 4) /* continuous clock
SND_SOC_DAIFMT_GATED (2 << 4) /* clock is gated
[8 : 11] 用于设置接口的格式
SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame
SND_SOC_DAIFMT_NB_IF (2 << 8) /* normal BCLK + inv FRM
SND_SOC_DAIFMT_IB_NF (3 << 8) /* invert BCLK + nor FRM
SND_SOC_DAIFMT_IB_IF (4 << 8) /* invert BCLK + FRM
[12 : 15] 用于设置接口的格式
SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & FRM master
SND_SOC_DAIFMT_CBS_CFM (2 << 12) /* codec clk slave & FRM master
SND_SOC_DAIFMT_CBM_CFS (3 << 12) /* codec clk master & frame slave
SND_SOC_DAIFMT_CBS_CFS (4 << 12) /* codec clk & FRM slave */
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct snd_soc_dai {
const char *name;/* dai的名字 */
int id;
struct device *dev;/* 设备指针 */
/* driver ops */
struct snd_soc_dai_driver *driver;/* 指向dai驱动结构的指针 */
/* DAI runtime info */
unsigned int capture_active; /* stream usage count */
unsigned int playback_active; /* stream usage count */
unsigned int probed:1;
unsigned int active;
struct snd_soc_dapm_widget *playback_widget;/* dapm部件指针 */
struct snd_soc_dapm_widget *capture_widget;/* dapm部件指针 */
/* DAI DMA data */
void *playback_dma_data;/* 用于管理playback dma */
void *capture_dma_data;/* 用于管理capture dma */
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;//采样率
unsigned int channels;//通道数
unsigned int sample_bits;//采样位数
/* parent platform/codec */
struct snd_soc_component *component;//所属控件
/* CODEC TDM slot masks and params (for fixup) */
unsigned int tx_mask;
unsigned int rx_mask;
struct list_head list;
};
struct snd_soc_dai_driver {
/* DAI description */
const char *name;/* dai驱动名字 */
unsigned int id;/* dai驱动ID */
unsigned int base;
struct snd_soc_dobj dobj;
/* DAI driver callbacks */
int (*probe)(struct snd_soc_dai *dai);/* dai驱动的probe函数,由snd_soc_instantiate_card回调 */
int (*remove)(struct snd_soc_dai *dai);
int (*suspend)(struct snd_soc_dai *dai);/* 电源挂起 */
int (*resume)(struct snd_soc_dai *dai);/* 电源恢复 */
/* compress dai */
int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
/* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);
/* ops */
const struct snd_soc_dai_ops *ops;/* 指向本dai的snd_soc_dai_ops结构 */
const struct snd_soc_cdai_ops *cops;
/* DAI capabilities */
struct snd_soc_pcm_stream capture; /* 描述capture的能力 */
struct snd_soc_pcm_stream playback; /* 描述playback的能力 */
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
unsigned int bus_control:1; /* DAI is also used for the control bus */
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
};
struct snd_soc_component {
const char *name;/*component名字 也就是codec名字或者platform名字*/
int id;
const char *name_prefix;
struct device *dev;/*component所属设备*/
struct snd_soc_card *card;/* 指向Machine驱动的card实例 */
unsigned int active;
unsigned int suspended:1; /* is in suspend PM state */
struct list_head list;
struct list_head card_aux_list; /* for auxiliary bound components */
struct list_head card_list;
const struct snd_soc_component_driver *driver;/* 指向该component_driver指针 */
struct list_head dai_list;/*用于管理snd_soc_dai的链表*/
int num_dai;/*拥有snd_soc_dai个数*/
struct regmap *regmap;
int val_bytes;
struct mutex io_mutex;
/* attached dynamic objects */
struct list_head dobj_list;
/*
* DO NOT use any of the fields below in drivers, they are temporary and
* are going to be removed again soon. If you use them in driver code
* the driver will be marked as BROKEN when these fields are removed.
*/
/* Don't use these, use snd_soc_component_get_dapm() */
struct snd_soc_dapm_context dapm;
/* machine specific init */
int (*init)(struct snd_soc_component *component);
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
const char *debugfs_prefix;
#endif
};
struct snd_soc_component_driver {
const char *name;/*component_driver 名*/
/* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls; /* 音频控件指针 */
unsigned int num_controls;/*总个数*/
const struct snd_soc_dapm_widget *dapm_widgets;/* dapm部件指针 */
unsigned int num_dapm_widgets;/*总个数*/
const struct snd_soc_dapm_route *dapm_routes;/* dapm路线指针 */
unsigned int num_dapm_routes;/*总个数*/
/*加载卸载 电源管理函数*/
int (*probe)(struct snd_soc_component *component);
void (*remove)(struct snd_soc_component *component);
int (*suspend)(struct snd_soc_component *component);
int (*resume)(struct snd_soc_component *component);
/* 读写寄存器函数 */
unsigned int (*read)(struct snd_soc_component *component,
unsigned int reg);
int (*write)(struct snd_soc_component *component,
unsigned int reg, unsigned int val);
/* PCM申请和释放函数 */
/* pcm creation and destruction */
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd);
void (*pcm_free)(struct snd_pcm *pcm);
/* 时钟配置函数 */
/* component wide operations */
int (*set_sysclk)(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out);
int (*set_jack)(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
/* DT */
int (*of_xlate_dai_name)(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name);
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint);
void (*seq_notifier)(struct snd_soc_component *component,
enum snd_soc_dapm_type type, int subseq);
int (*stream_event)(struct snd_soc_component *component, int event);
int (*set_bias_level)(struct snd_soc_component *component,
enum snd_soc_bias_level level);/* 偏置电压配置函数 */
const struct snd_pcm_ops *ops;
const struct snd_compr_ops *compr_ops;
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
/*
* signal if the module handling the component should not be removed
* if a pcm is open. Setting this would prevent the module
* refcount being incremented in probe() but allow it be incremented
* when a pcm is opened and decremented when it is closed.
*/
unsigned int module_get_upon_open:1;
/* bits */
unsigned int idle_bias_on:1;
unsigned int suspend_bias_off:1;
unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
unsigned int endianness:1;
unsigned int non_legacy_dai_naming:1;
/* this component uses topology and ignore machine driver FEs */
const char *ignore_machine;
const char *topology_name_prefix;
int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params);
bool use_dai_pcm_id; /* use DAI link PCM ID as PCM device number */
int be_pcm_base; /* base device ID for all BE PCMs */
};
以openwrt21.02 kernel5.4中的/sound/soc/ralink/ralink-i2s.c为例:
struct rt_i2s_data {
u32 flags;
void (*refclk_setup)(void);
};
static const struct snd_soc_dai_ops ralink_i2s_dai_ops = {
.set_sysclk = ralink_i2s_set_sysclk,
.set_fmt = ralink_i2s_set_fmt,
.startup = ralink_i2s_startup,
.shutdown = ralink_i2s_shutdown,
.hw_params = ralink_i2s_hw_params,
.trigger = ralink_i2s_trigger,
};
static struct snd_soc_dai_driver ralink_i2s_dai = {
.name = "ralink-i2s",
.probe = ralink_i2s_dai_probe,
.remove = ralink_i2s_dai_remove,
.ops = &ralink_i2s_dai_ops,
.capture = {
.stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rate_min = 5512,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.playback = {
.stream_name = "I2S Playback",
.channels_min = 2,
.channels_max = 2,
.rate_min = 5512,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.symmetric_rates = 1,
};
/*
info:标明了PCM类型和能力
SNDRV_PCM_INFO_MMAP 支持mmap
交错格式,或者非交错格式SNDRV_PCM_INFO_INTERLEAVED 和SNDRV_PCM_INFO_NONINTERLEAVED
formats:支持的格式
rates:支持的速率
channel_min和channe_max:最小,最大通道数
period_byte 周期大小
periods 周期数量
buffer_bytes_max最大buffer大小,单位字节
*/
static struct snd_pcm_hardware ralink_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_S16_LE,
.channels_min = 2,
.channels_max = 2,
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = PAGE_SIZE * 2,
.periods_min = 2,
.periods_max = 128,
.buffer_bytes_max = 128 * 1024,
.fifo_size = RALINK_I2S_FIFO_SIZE,
};
static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = {
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.pcm_hardware = &ralink_pcm_hardware,
.prealloc_buffer_size = 256 * PAGE_SIZE,
};
/*name用于machine dai_link匹配platform驱动*/
static const struct snd_soc_component_driver ralink_i2s_component = {
.name = "ralink-i2s",
};
struct rt_i2s_data mt7628_i2s_data = {
.flags = (RALINK_FLAGS_ENDIAN | RALINK_FLAGS_24BIT |
RALINK_FLAGS_LEFT_J),
.refclk_setup = mt7628_refclk_setup};
static const struct of_device_id ralink_i2s_match_table[] = {
...
{ .compatible = "mediatek,mt7628-i2s",
.data = (void *)&mt7628_i2s_data },
};
static int ralink_i2s_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *np = pdev->dev.of_node;
struct ralink_i2s *i2s;
struct resource *res;
int irq, ret;
u32 dma_req;
struct rt_i2s_data *data;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
platform_set_drvdata(pdev, i2s);
i2s->dev = &pdev->dev;
match = of_match_device(ralink_i2s_match_table, &pdev->dev);
if (!match)
return -EINVAL;
data = (struct rt_i2s_data *)match->data;
i2s->flags = data->flags;
/* setup out 12Mhz refclk to codec as mclk */
if (data->refclk_setup)
data->refclk_setup();
/*获得i2s设备树 dma 的ID*/
if (of_property_read_u32(np, "txdma-req", &dma_req)) {
dev_err(&pdev->dev, "no txdma-req define\n");
return -EINVAL;
}
i2s->txdma_req = (u16)dma_req;
if (!(i2s->flags & RALINK_FLAGS_TXONLY)) {
if (of_property_read_u32(np, "rxdma-req", &dma_req)) {
dev_err(&pdev->dev, "no rxdma-req define\n");
return -EINVAL;
}
i2s->rxdma_req = (u16)dma_req;
}
/*获得I2S寄存器基地址*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2s->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2s->regs))
return PTR_ERR(i2s->regs);
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs,
&ralink_i2s_regmap_config);
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev, "regmap init failed\n");
return PTR_ERR(i2s->regmap);
}
/*获得i2s的中断号*/
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
return -EINVAL;
}
#if (RALINK_I2S_INT_EN)
ret = devm_request_irq(&pdev->dev, irq, ralink_i2s_irq,
0, dev_name(&pdev->dev), i2s);
if (ret) {
dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
#endif
/*获得I2S的时钟*/
i2s->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2s->clk)) {
dev_err(&pdev->dev, "no clock defined\n");
return PTR_ERR(i2s->clk);
}
/*使能时钟*/
ret = clk_prepare_enable(i2s->clk);
if (ret)
return ret;
/*初始化DMA的目标地址,dma传送单元大小和通道号等*/
ralink_i2s_init_dma_data(i2s, res);
device_reset(&pdev->dev);
ret = ralink_i2s_debugfs_create(i2s);
if (ret) {
dev_err(&pdev->dev, "create debugfs failed\n");
goto err_clk_disable;
}
/* dormat支持24bit */
if (i2s->flags & RALINK_FLAGS_24BIT) {
ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S24_LE;
ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S24_LE;
}
/* 大端模式*/
if (i2s->flags & RALINK_FLAGS_ENDIAN) {
ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S16_BE;
ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S16_BE;
ralink_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S16_BE;
if (i2s->flags & RALINK_FLAGS_24BIT) {
ralink_i2s_dai.capture.formats |=
SNDRV_PCM_FMTBIT_S24_BE;
ralink_i2s_dai.playback.formats |=
SNDRV_PCM_FMTBIT_S24_BE;
ralink_pcm_hardware.formats |=
SNDRV_PCM_FMTBIT_S24_BE;
}
}
/* disable capture support */
if (i2s->flags & RALINK_FLAGS_TXONLY)
memset(&ralink_i2s_dai.capture, sizeof(ralink_i2s_dai.capture),
0);
/*注册控件cpu_dai*/
ret = devm_snd_soc_register_component(&pdev->dev, &ralink_i2s_component,
&ralink_i2s_dai, 1);
if (ret)
goto err_debugfs;
/*注册DMA控件*/
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
&ralink_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_COMPAT);
if (ret)
goto err_debugfs;
dev_info(i2s->dev, "mclk %luMHz\n", clk_get_rate(i2s->clk) / 1000000);
return 0;
err_debugfs:
ralink_i2s_debugfs_remove(i2s);
err_clk_disable:
clk_disable_unprepare(i2s->clk);
return ret;
}
devm_snd_soc_register_component()
->snd_soc_register_component()
->snd_soc_add_component()
->snd_soc_component_initialize()
->snd_soc_register_dais()
->soc_add_dai()
->list_add_tail(&dai->list, &component->dai_list);
->snd_soc_component_add(component);
->list_add(&component->list, &component_list);
devm_snd_dmaengine_pcm_register()
->snd_dmaengine_pcm_register()
->snd_soc_add_component(dev, &pcm->component,&dmaengine_pcm_component, NULL, 0);
->snd_soc_component_initialize()
->snd_soc_register_dais()
->soc_add_dai()
->list_add_tail(&dai->list, &component->dai_list);
->snd_soc_component_add(component);
->list_add(&component->list, &component_list);
以openwrt21.02 kernel5.4中的/sound/soc/codecs/wm8960.c为例:
static const struct regmap_config wm8960_regmap = {
.reg_bits = 7,//寄存器地址位数
.val_bits = 9,//数据位数
.max_register = WM8960_PLL4,
.reg_defaults = wm8960_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = wm8960_volatile,
};
static int wm8960_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8960_priv *wm8960;
int ret;
/*为结构体分配内存并清零*/
wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
GFP_KERNEL);
if (wm8960 == NULL)
return -ENOMEM;
/*获取mclk*/
wm8960->mclk = devm_clk_get(&i2c->dev, "mclk");
if (IS_ERR(wm8960->mclk)) {
if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
/*wm8960 i2c的regmap子系统获取 用于寄存器读写*/
wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
if (IS_ERR(wm8960->regmap))
return PTR_ERR(wm8960->regmap);
if (pdata)
memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data));
else if (i2c->dev.of_node)
wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
ret = wm8960_reset(wm8960->regmap);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to issue reset\n");
return ret;
}
if (wm8960->pdata.shared_lrclk) {
ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2,
0x4, 0x4);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable LRCM: %d\n",
ret);
return ret;
}
}
/* Latch the update bits */
regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
i2c_set_clientdata(i2c, wm8960);
/*注册一个codec 组件 流程和上面一样*/
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_wm8960, &wm8960_dai, 1);
return ret;
}
static const struct of_device_id wm8960_of_match[] = {
{ .compatible = "wlf,wm8960", },
{ }
};
static struct i2c_driver wm8960_i2c_driver = {
.driver = {
.name = "wm8960",
.of_match_table = wm8960_of_match,
},
.probe = wm8960_i2c_probe,
.remove = wm8960_i2c_remove,
.id_table = wm8960_i2c_id,
};
static int wm8960_probe(struct snd_soc_component *component)
{
struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
struct wm8960_data *pdata = &wm8960->pdata;
if (pdata->capless)
wm8960->set_bias_level = wm8960_set_bias_level_capless;
else
wm8960->set_bias_level = wm8960_set_bias_level_out3;
snd_soc_add_component_controls(component, wm8960_snd_controls,
ARRAY_SIZE(wm8960_snd_controls));/*添加控件*/
wm8960_add_widgets(component);/*添加DAPM控件*/
return 0;
}
static const struct snd_soc_component_driver soc_component_dev_wm8960 = {
.probe = wm8960_probe,
.set_bias_level = wm8960_set_bias_level,
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static const struct snd_soc_dai_ops wm8960_dai_ops = {
.hw_params = wm8960_hw_params,
.hw_free = wm8960_hw_free,
.digital_mute = wm8960_mute,
.set_fmt = wm8960_set_dai_fmt,
.set_clkdiv = wm8960_set_dai_clkdiv,
.set_pll = wm8960_set_dai_pll,
.set_sysclk = wm8960_set_dai_sysclk,
};
static struct snd_soc_dai_driver wm8960_dai = {
.name = "wm8960-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.ops = &wm8960_dai_ops,
.symmetric_rates = 1,
};