rk3399_android7.1耳机拔插ADC检测

我们这里以rk3399 android7.1平台为例,浅析adc接口实现检测耳机的拔插动作。

硬件原理部分:耳机插入把PHE_DET1处的弹片弹开,然后实现headphone_dect直接连通到1.8v,检测端得到高电平1.8v;拔出耳机,弹片回去,只有0.159v电压。所以实现了没有耳机插入时这个“ADC_IN4”为低电位,插入耳机时这个“ADC_IN4”为高电位

rk3399_android7.1耳机拔插ADC检测_第1张图片
rk3399_android7.1耳机拔插ADC检测_第2张图片
耳机实物图:
rk3399_android7.1耳机拔插ADC检测_第3张图片

软件实现部分:
1、kernel

+ //dts配置
+       rockchip_headset {
+               compatible = "rockchip_headset";
+               io-channels = <&saradc 4>; //硬件上接的是adc4
+               io-channel-names = "headset";
+               status = "okay";
+       };

驱动:

drivers/headset_observe/rk_headset_irq_hook_adc.c
drivers/headset_observe/rockchip_headset_core.c

驱动代码里probe首先注册switch这个子类,并提供switch_dev_register这个注册入口,对应的设备驱动调用switch_dev_register把自己注册在switch这个子类中,ret = switch_dev_register(&switch_data_headset->sdev);然后在/sys/class/switch目录中就会生成h2w这个子目录,里面会有state,name等一些成员,然后初始化一个工作队列,设置一个时间,循环去执行工作队列去读取ADC 的值,根据读到的值调用switch_set_state函数改变state状态值,然后上层通过读取这个节点的值来相应切换状态。

文件:./drivers/headset_observe/rockchip_headset_core.c

rockchip_headset_of_match的compatible跟dts的compatible匹配
--> 执行rockchip_headset_probe
	-->kzalloc(sizeof(struct rk_headset_pdata), GFP_KERNEL); //分配内存给私有结构体rk_headset_pdata
		-->ret = of_get_named_gpio_flags(node, "headset_gpio", 0, &flags); //解析gpio
		如果解析到gpio就devm_gpio_request //申请GPIO,gpio_direction_input//设置GPIO口为输入状态
		
		-->of_get_named_gpio_flags(node, "hook_gpio", 0, &pdata->hook_gpio); //解析GPIO
		如果解析不到GPIO就 pdata->chan = iio_channel_get(&pdev->dev, NULL); //获取ADC通道
	
			-->if(pdata->chan != NULL) //如果获取到ADC channel就执行ADC检测耳机拔插的probe探测函数
				-->ret = rk_headset_adc_probe(pdev,pdata);
			-->否则就执行ret = rk_headset_probe(pdev,pdata); //GPIO检测耳机拔插模式的probe探测函数



文件:drivers/headset_observe/rk_headset_irq_hook_adc.c
//宏定义上报给上层的state值
+#define BIT_HEADSET             (0 << 1) //no headphone insertion
+#define BIT_HEADSET_MIC         (1 << 0) //have mic headphone insertion
+#define BIT_HEADSET_NO_MIC      (1 << 1) //no mic headphone insertion

rk_headset_adc_probe
->ret = switch_dev_register(&headset->sdev); //注册switch这个子类
		->INIT_DELAYED_WORK(&headset->h_delayed_work[HOOK], hook_once_work);
			->iio_read_channel_raw(headset_info->chan, &val);
				->headset_info->cur_headset_status = headset_info->isMic ? BIT_HEADSET:BIT_HEADSET_NO_MIC;
					->switch_set_state(&headset_info->sdev, headset_info->cur_headset_status);//这里设置耳机的状态值
						->schedule_delayed_work(&headset_info->h_delayed_work[HOOK], msecs_to_jiffies(750));//循环750ms执行一次工作队列

shell环境下通过执行cat sys/class/switch/h2w/state 查看耳机插入状态(上层通过获取这个state值去返回对应的状态,显示相应的图标):

rk3399_all:/ #  cat sys/class/switch/h2w/state 
state <= 0 表示无耳机插入 
state = 1 表示带 Mic 耳机插入 
state = 2 表示不带 Mic 耳机插入

在system/media/brillo/audio/audioservice/audio_device_handler.cpp获取状态更新给上层调用,直接读取sys/class/switch/h2w/中state值来获取最新的状态(以下为截取的部分代码)。


  static const char kH2WStateFile[] = "/sys/class/switch/h2w/state";
  
 63 void AudioDeviceHandler::GetInitialAudioDeviceState(
 64     const base::FilePath& path) {
 65   base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
 66   if (!file.IsValid()) {
 67     LOG(WARNING) << "Kernel does not have wired headset support. Could not "
 68                  << "open " << path.value() << "( "
 69                  << base::File::ErrorToString(file.error_details()) << " ).";
 70     return;
 71   }
 72   int state = 0;
 73   int bytes_read = file.ReadAtCurrentPos(reinterpret_cast<char*>(&state), 1);
 74   state -= '0';
 75   if (bytes_read == 0) {
 76     LOG(WARNING) << "Could not read from " << path.value();
 77     return;
 78   }
 79   VLOG(1) << "Initial audio jack state is " << state;
 80   static const int kHeadPhoneMask = 0x1;
 81   bool headphone = state & kHeadPhoneMask;
 82   static const int kMicrophoneMask = 0x2;
 83   bool microphone = (state & kMicrophoneMask) >> 1;
 84
 85   UpdateAudioSystem(headphone, microphone);
 86 }

你可能感兴趣的:(RK系列驱动开发)