WCD9335 audio driver Probe函数分析

本文详细解析了Wcd9335 Codec在Linux内核中的初始化过程,包括模块加载入口、私有数据分配、工作队列与互斥锁初始化、资源管理器配置等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码位于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;
};






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值