源码位于kernel\sound\soc\codecs\Wcd9335.c
1. 模块初始化入口代码放在了module_platform_driver()这个函数中,关于这个函数的解析,请看博客链接如下,
http://blog.youkuaiyun.com/jgw2008/article/details/52690602
module_platform_driver(tasha_codec_driver);
tasha_codec_driver结构体如下,模块加载入口从"tasha_probe"开始
static struct platform_driver tasha_codec_driver = {
.probe = tasha_probe,
.remove = tasha_remove,
.shutdown = tasha_powershutdown,
.driver = {
.name = "tasha_codec",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &tasha_pm_ops,
#endif
},
};
2. 下面分析函数"tasha_probe"
2.1 为tasha private data(私有数据)申请空间,并保存起来,
关于platform_set_drvdata的分析,请参见博文http://blog.youkuaiyun.com/jgw2008/article/details/52692616
tasha = devm_kzalloc(&pdev->dev, sizeof(struct tasha_priv),
GFP_KERNEL);
if (!tasha) {
dev_err(&pdev->dev, "%s: cannot create memory for wcd9335\n",
__func__);
return -ENOMEM;
}
platform_set_drvdata(pdev, tasha);
2.2 下面是tasha结构体初始化
注意:
a. 初始了2了工作队列,一个是跟power有关;一个是跟control有关。
b. 初始了6个mutex。
关于队列的分析介绍,请参见博文http://blog.youkuaiyun.com/jgw2008/article/details/52693268
关于devm_kzalloc的使用,参加博文http://blog.youkuaiyun.com/jgw2008/article/details/52691568
tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
tasha->dev = &pdev->dev;
INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work);
mutex_init(&tasha->power_lock);
mutex_init(&tasha->sido_lock);
INIT_WORK(&tasha->swr_add_devices_work, wcd_swr_ctrl_add_devices);
BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier);
mutex_init(&tasha->micb_lock);
mutex_init(&tasha->swr_read_lock);
mutex_init(&tasha->swr_write_lock);
mutex_init(&tasha->swr_clk_lock);
cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
GFP_KERNEL);
if (!cdc_pwr) {
ret = -ENOMEM;
goto cdc_pwr_fail;
}
tasha->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr;
cdc_pwr->pwr_collapse_reg_min = TASHA_DIG_CORE_REG_MIN;
cdc_pwr->pwr_collapse_reg_max = TASHA_DIG_CORE_REG_MAX;
wcd9xxx_set_power_state(tasha->wcd9xxx,
WCD_REGION_POWER_COLLAPSE_REMOVE,
WCD9XXX_DIG_CORE_REGION_1);
2.3 向系统注册codec
关于codec注册的分析,可以参阅博文http://blog.youkuaiyun.com/jgw2008/article/details/52799186
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha,
tasha_dai, ARRAY_SIZE(tasha_dai));
if (ret) {
dev_err(&pdev->dev, "%s: Codec registration failed\n",
__func__);
goto cdc_reg_fail;
}
}
注意,snd_soc_register_codec的第二个参数"soc_codec_dev_tasha"的定义如下,其结构体成员将会赋值给结构体”snd_soc_codec“。
这个结构体里还有一个codec probe函数"tasha_codec_probe"不知道什么时候被呼叫,但无论如何,codec probe一定会被呼叫,而且是十分重要的函数。
关于codec probe函数"tasha_codec_probe"的分析,请参考博文http://blog.youkuaiyun.com/jgw2008/article/details/52818081
static struct snd_soc_codec_driver soc_codec_dev_tasha = {
.probe = tasha_codec_probe,
.remove = tasha_codec_remove,
.suspend = tasha_codec_suspend,
.resume = tasha_codec_resume,
.controls = tasha_snd_controls,
.num_controls = ARRAY_SIZE(tasha_snd_controls),
.dapm_widgets = tasha_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tasha_dapm_widgets),
.dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map),
};
2.4 初始化资源管理器
/*
* Init resource manager so that if child nodes such as SoundWire
* requests for clock, resource manager can honor the request
*/
resmgr = wcd_resmgr_init(&tasha->wcd9xxx->core_res, NULL);
if (IS_ERR(resmgr)) {
ret = PTR_ERR(resmgr);
dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n",
__func__);
goto unregister_codec;
}
tasha->resmgr = resmgr;
2.5 初始化 读/写/中断等的回调函数,十分重要
tasha->swr_plat_data.handle = (void *) tasha;
tasha->swr_plat_data.read = tasha_swrm_read;
tasha->swr_plat_data.write = tasha_swrm_write;
tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write;
tasha->swr_plat_data.clk = tasha_swrm_clock;
tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq;
2.6 注册clock
/* Register for Clock */
wcd_ext_clk = clk_get(tasha->wcd9xxx->dev, "wcd_clk");
if (IS_ERR(wcd_ext_clk)) {
dev_err(tasha->wcd9xxx->dev, "%s: clk get %s failed\n",
__func__, "wcd_ext_clk");
goto resmgr_remove;
}
tasha->wcd_ext_clk = wcd_ext_clk;
2.7 tasha其他设置,如电压等
tasha->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV;
set_bit(AUDIO_NOMINAL, &tasha->status_mask);
tasha->sido_ccl_cnt = 0;
2.8 更新code默认值
/* Update codec register default values */
tasha_update_reg_defaults(tasha);
2.9 添加设备(为什么要使用队列呢?)
关于添加设备到device list, 请见博文http://blog.youkuaiyun.com/jgw2008/article/details/52815271
schedule_work(&tasha->swr_add_devices_work);
2.10 获取codec版本
tasha_get_codec_ver(tasha);
至此,codec和DAIs(Digital Audio Interface)已经成功注册,probe函数跑完!
另外,研究一个驱动或者函数,其中涉及的结构体最为重要,在函数tasha_probe()中,主要涉及的结构体是"tasha_priv", 函数完成了对这个结构体的填充和初始化。
下面是结构体的定义
struct tasha_priv {
struct device *dev;
struct wcd9xxx *wcd9xxx;
struct snd_soc_codec *codec;
u32 adc_count;
u32 rx_bias_count;
s32 dmic_0_1_clk_cnt;
s32 dmic_2_3_clk_cnt;
s32 dmic_4_5_clk_cnt;
s32 ldo_h_users;
s32 micb_ref[TASHA_MAX_MICBIAS];
s32 pullup_ref[TASHA_MAX_MICBIAS];
u32 anc_slot;
bool anc_func;
/* Vbat module */
struct wcd_vbat vbat;
/* cal info for codec */
struct fw_info *fw_data;
/*track tasha interface type*/
u8 intf_type;
/* num of slim ports required */
struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/* SoundWire data structure */
struct tasha_swr_ctrl_data *swr_ctrl_data;
int nr;
/*compander*/
int comp_enabled[COMPANDER_MAX];
/* Maintain the status of AUX PGA */
int aux_pga_cnt;
u8 aux_l_gain;
u8 aux_r_gain;
bool spkr_pa_widget_on;
struct regulator *spkdrv_reg;
struct regulator *spkdrv2_reg;
bool mbhc_started;
/* class h specific data */
struct wcd_clsh_cdc_data clsh_d;
struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
/*
* list used to save/restore registers at start and
* end of impedance measurement
*/
struct list_head reg_save_restore;
/* handle to cpe core */
struct wcd_cpe_core *cpe_core;
u32 current_cpe_clk_freq;
enum tasha_sido_voltage sido_voltage;
int sido_ccl_cnt;
u32 ana_rx_supplies;
/* Multiplication factor used for impedance detection */
int zdet_gain_mul_fact;
/* to track the status */
unsigned long status_mask;
struct work_struct swr_add_devices_work;
struct wcd_swr_ctrl_platform_data swr_plat_data;
/* Port values for Rx and Tx codec_dai */
unsigned int rx_port_value;
unsigned int tx_port_value;
unsigned int vi_feed_value;
/* Tasha Interpolator Mode Select for EAR, HPH_L and HPH_R */
u32 hph_mode;
u16 prim_int_users[TASHA_NUM_INTERPOLATORS];
int spl_src_users[SPLINE_SRC_MAX];
struct wcd9xxx_resmgr_v2 *resmgr;
struct delayed_work power_gate_work;
struct mutex power_lock;
struct mutex sido_lock;
/* mbhc module */
struct wcd_mbhc mbhc;
struct blocking_notifier_head notifier;
struct mutex micb_lock;
struct clk *wcd_ext_clk;
struct mutex swr_read_lock;
struct mutex swr_write_lock;
struct mutex swr_clk_lock;
int swr_clk_users;
int power_active_ref;
int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high);
struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
enum wcd9335_codec_event);
struct snd_info_entry *entry;
struct snd_info_entry *version_entry;
int spkr_gain_offset;
int spkr_mode;
struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS];
struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS];
int hph_l_gain;
int hph_r_gain;
int rx_7_count;
int rx_8_count;
};