(本文仅用于本人学习记录,仅供参考)
一、耳机基本认识

引用http://www.cnblogs.com/Peter-Chen/p/3999212.html(稍作修改)
主要有两种耳机类型,从下图可以看到,左边的耳机接口结构有左声道、右声道、接地端和mic端,下面我们称该类型耳机为四环耳机;右边的耳机接口结构有左声道、右声道、接地端,不带mic接口,我们称之为三环耳机,区别就在于带不带麦克风。

与耳机对应,一般常见的耳机插口都是由5PIN or 6PIN组成,其中PIN脚分别作为HP_OUTL(左声道输出)、HP_OUTR(右声道输出)、HP_DET#(耳机检测)、GROUND(地) & MIC(麦克风)使用。
驱动中耳机检测的一般流程:
HP_DET#信号由High->Low,触发IRQ到SOC,进入中断处理函数(即耳机类型检测);先是去检测耳机的类型,确定耳机是三环还是四环耳机,将耳机状态上报给系统后,再去检测按键状态,当检测到按键状态有变化,通过输入子系统上报事件,然后进行相对应的按键功能处理。
  虽然耳机插口仅由5~6PIN组成,但不同的厂商会将其设计成各种各样。耳机接口的不同构造会对应着不同现象。以下是在网上搜集的一些接口类型,仅供参考。
  
类型一:

该耳机插口构造是HP_DET#和HPOUTL处于最前端,并且在同一位置,这样的设计是只有当耳机全部插入的时候,才能触发HP_DET#信号。由耳机检测的流程可知,该构造可以很精确的确定耳机是三环还是四环耳机。并且不管快拔 or 慢拔耳机,都能第一时间触发HP_DET#信号。

类型二:

这样的构造可能会出现当4环耳机刚拔出1环左右长度时,在这种情况下,6个PIN的状态可能如下:HP_OUTL处于float状态;HP_DET# & HS_OUTR处于第一环;AGND处于第二环;MIC2P_HP处于第三环,恰好是耳机的“-G-”,使得MIC2P_HP引脚的电压值为0.0V。由于HP_DET#依旧处于Pull Low状态,这样系统认为4环耳机仍然插入,而此时MIC2P_HP引脚因接地,导致其引脚电压值一直为0.0V,这与插入4环耳机,长按Butten的效果是一样的。

类型三:

类型三耳机插口构造是将HP_DET# & AGND引脚接到同一位置,这样当耳机插入2节左右长度时,系统就可以识别到有耳机插入。使用Type3 Jack除了会出现Type2 Jack问题外,还会出现当慢慢斜着插入3环耳机时,系统会认为是4环耳机直接上报给上层。这个问题可以通过机构的设计解决“斜插”的问题,从而消除3环误认为4环的现象。

类型四:

该构造与类型一的构造和效果差不多。

二、主要代码函数分析
1、cod3034x芯片的probe回调函数,进行芯片的初始化,用工作队列创建耳机类型和按键的状态检测函数。以下贴出的是部分代码

/*Codec芯片的probe函数*/
cod3034x_codec_probe(struct snd_soc_codec *codec)|-- snd_soc_codec_get_drvdata(codec);|-- devm_regulator_get(codec->dev, "vdd_ldo27");|-- INIT_WORK(&cod3034x->buttons_work, cod3034x_buttons_work);//添加按键检测的工作队列|-- cod3034x_buttons_work//耳机按键检测函数|-- create_singlethread_workqueue("buttons_wq");|-- INIT_WORK(&cod3034x->jack_det_work, cod3034x_jack_det_work);//添加耳机检测的工作队列|-- cod3034x_jack_det_work//耳机检测函数|-- create_singlethread_workqueue("jack_det_wq");|-- cod3034x_adc_start(cod3034x);|-- cod3034x_i2c_parse_dt(cod3034x);//获取dts中的设备属性值|-- cod3034x_register_notifier(&codec_notifier, cod3034x);|-- set_codec_notifier_flag();|-- cod3034x_codec_initialize(codec);|-- cod3034x_jack_mic_register(codec);|-- cod3034x_enable(codec->dev);

2、cod3034x_jack_det_work函数用于检测插入耳机的类型,确定插入的是三段耳机还是四段耳机,三段与四段的区别在于耳机是否带麦克风。

static void cod3034x_jack_det_work(struct work_struct *work)
{dev_dbg(cod3034x->dev, "%s(%d) jackdet: %d\n",__func__, __LINE__, jackdet->jack_det);mutex_lock(&cod3034x->jackdet_lock);if (jackdet->jack_det == true) {/* set delay for read correct adc value */msleep(cod3034x->mic_det_delay);/* read adc for mic detect */adc = cod3034x_adc_get_value(cod3034x);//获取耳机接口的adc值dev_dbg(cod3034x->dev, "%s mic det adc  %d, mic_det_delay: %d\n",__func__, adc, cod3034x->mic_det_delay);if (adc > cod3034x->mic_adc_range)//判断是否检测到了micjackdet->mic_det = true;elsejackdet->mic_det = false;dev_dbg(cod3034x->dev, "%s Mic det: %d\n",__func__, jackdet->mic_det);jackdet->adc_val = adc;} else {dev_dbg(cod3034x->dev, "%s JACK OUT\n", __func__);/* jack/mic out */jackdet->mic_det = false;jackdet->adc_val = -EINVAL;}/* 发送耳机接口事件给音频框架 */if (jackdet->jack_det && jackdet->mic_det)//switch_set_state(&cod3034x->sdev, 1);   /* 4段耳机 */else if (jackdet->jack_det)switch_set_state(&cod3034x->sdev, 2);   /* 3段耳机 */elseswitch_set_state(&cod3034x->sdev, 0);if (cod3034x->is_suspend)regcache_cache_only(cod3034x->regmap, false);snd_soc_write(codec, COD3034X_92_JACK_CTR, 0x30);if (jackdet->jack_det && jackdet->mic_det) {/* 4 Pole Jack-in */snd_soc_write(codec, COD3034X_92_JACK_CTR, 0x30);/* button threshold */snd_soc_write(codec, COD3034X_88_CTR_IMP3, 0x10);dev_dbg(cod3034x->dev, "%s 4 Pole Jack-In\n", __func__);} else if (jackdet->jack_det) {/* 3 Pole Jack-in */snd_soc_write(codec, COD3034X_92_JACK_CTR, 0x20);/* button threshold */snd_soc_write(codec, COD3034X_88_CTR_IMP3, 0x10);dev_dbg(cod3034x->dev, "%s 3 Pole Jack-In\n", __func__);} else {/* Jack-out */snd_soc_write(codec, COD3034X_92_JACK_CTR, 0x00);dev_dbg(cod3034x->dev, "%s JACK OUT\n", __func__);}if (cod3034x->is_suspend)regcache_cache_only(cod3034x->regmap, true);dev_dbg(cod3034x->codec->dev, "Jack %s, Mic %s\n",jackdet->jack_det ? "inserted" : "removed",jackdet->mic_det ? "inserted" : "removed");mutex_unlock(&cod3034x->jackdet_lock);
}

3、cod3034x的按键检测函数

static void cod3034x_buttons_work(struct work_struct *work)
{/*按键按下但是按键突然拔出情况下的处理*/if (!jd->jack_det) {dev_err(cod3034x->dev, "Skip button events for jack_out\n");/* button pressed and earjack out in sudden, button should be released. */if (jd->privious_button_state == BUTTON_PRESS) {jd->button_det = false;input_report_key(cod3034x->input, jd->button_code, 0);input_sync(cod3034x->input);cod3034x_process_button_ev(cod3034x->codec, jd->button_code, 0);dev_dbg(cod3034x->dev, ":key %d released when jack_out\n", jd->button_code);}return;}/*如果只检测到了耳机但是没有检测到麦克风,说明是三段耳机,按键检测函数直接结束*/if (!jd->mic_det) {dev_err(cod3034x->dev, "Skip button events for 3-pole jack\n");return;}/*确保耳机插入无误后进行按键的检测*//* set delay for read correct adc value */mdelay(10);for (j = 0; j < ADC_TRACE_NUM2; j++) {/* read GPADC for button */for (i = 0; i < ADC_TRACE_NUM; i++) {adc = cod3034x_adc_get_value(cod3034x);//获取耳机的adc值adc_values[i] = adc;udelay(ADC_READ_DELAY_US);}/** check avg/devi value is proper* if not read adc after 5 ms*/avg = get_adc_avg(adc_values);//求平均值devi = get_adc_devi(avg, adc_values);//(数据值-平均值)的平方和dev_dbg(cod3034x->dev,":button adc avg: %d, devi: %d\n", avg, devi);if (devi > ADC_DEVI_THRESHOLD) {queue_work(cod3034x->buttons_wq,&cod3034x->buttons_work);for (i = 0; i < ADC_TRACE_NUM;) {dev_err(cod3034x->dev,":retry button_work :  %d %d %d %d %d\n",adc_values[i + 0],adc_values[i + 1],adc_values[i + 2],adc_values[i + 3],adc_values[i + 4]);i += 5;}return;}adc_final_values[j] = avg;if (avg > adc_max)adc_max = avg;mdelay(ADC_READ_DELAY_MS);}adc_final = adc_max;/* 检测按键是处于按下状态还是松开状态 */if (adc_final > cod3034x->btn_release_value) {/*btn_release_value在cod3034x_i2c_parse_dt函数中可确定*/dev_dbg(cod3034x->dev,"Button Released! adc_fanal: %d, btn_value: %d\n",adc_final,cod3034x->btn_release_value);current_button_state = BUTTON_RELEASE;//确定当前按键状态为松开} else {dev_dbg(cod3034x->dev,"Button Pressed! adc_final: %d, btn_value: %d\n",adc_final,cod3034x->btn_release_value);current_button_state = BUTTON_PRESS;//}/*判断当前按键状态是否与前一按键状态相同,相同的话,直接返回不会再去检测adc值*/if (jd->privious_button_state == current_button_state) {dev_dbg(cod3034x->dev, "status are same\n");return;}jd->privious_button_state = current_button_state;/*检测到当前按键状态与前一按键状态不同,将当前按键状态传给保存前一按键状态的标志位,用于下次判断*/adc = adc_final;jd->adc_val = adc_final;for (i = 0; i < 4; i++)/*打印耳机各个按键相对应的code,adc范围值*/dev_dbg(cod3034x->dev,"[DEBUG]: adc(%d), buttons: code(%d), low(%d), high(%d)\n",adc,cod3034x->jack_buttons_zones[i].code,cod3034x->jack_buttons_zones[i].adc_low,cod3034x->jack_buttons_zones[i].adc_high);/* 根据设定的范围值去确定具体是哪个按键按下 */if (current_button_state == BUTTON_PRESS) {for (i = 0; i < num_buttons_zones; i++)if (adc >= btn_zones[i].adc_low &&adc <= btn_zones[i].adc_high) {jd->button_code = btn_zones[i].code;/*确定后通过输入子系统上报事件,产生中断实行按键功能*/input_report_key(cod3034x->input,jd->button_code,1);input_sync(cod3034x->input);jd->button_det = true;cod3034x_process_button_ev(cod3034x->codec,jd->button_code,1);dev_dbg(cod3034x->dev, ":key %d is pressed, adc %d\n",btn_zones[i].code, adc);return;}dev_dbg(cod3034x->dev, ":key skipped. ADC %d\n", adc);} else {/*按键松开事件上报中断*/jd->button_det = false;input_report_key(cod3034x->input, jd->button_code, 0);input_sync(cod3034x->input);cod3034x_process_button_ev(cod3034x->codec,jd->button_code,0);dev_dbg(cod3034x->dev, ":key %d released\n", jd->button_code);}return;
}

4、cod3034x_adc_get_value用于读取ADC接口的数据

static int cod3034x_adc_get_value(struct cod3034x_priv *cod3034x)
{int adc_data = -1;int adc_max = 0;int adc_min = 0xFFFF;int adc_total = 0;int adc_retry_cnt = 0;int i;struct iio_channel *jack_adc = cod3034x->jack_adc;for (i = 0; i < COD3034X_ADC_SAMPLE_SIZE; i++) {/*#define COD3034X_ADC_SAMPLE_SIZE 5*/iio_read_channel_raw(&jack_adc[0], &adc_data);/* 设定多次读取,直到读取成功 */while (adc_data < 0) {adc_retry_cnt++;if (adc_retry_cnt > 10)return adc_data;iio_read_channel_raw(&jack_adc[0], &adc_data);}/* Update min/max values */if (adc_data > adc_max)adc_max = adc_data;if (adc_data < adc_min)adc_min = adc_data;adc_total += adc_data;//获取五次的值进行相加}return (adc_total - adc_max - adc_min) / (COD3034X_ADC_SAMPLE_SIZE - 2);//减去五次数值中的最大值和最小值,求平均值。
}

5、cod3034x_i2c_parse_dt该函数用于从dts获取codec的各种属性值

static void cod3034x_i2c_parse_dt(struct cod3034x_priv *cod3034x)
{/* todo .. Need to add DT parsing for 3034 */struct device *dev = cod3034x->dev;struct device_node *np = dev->of_node;unsigned int bias_v_conf;int mic_range, mic_delay, btn_rel_val;struct of_phandle_args args;int i = 0;int ret;cod3034x->int_gpio = of_get_gpio(np, 0);if (cod3034x->int_gpio < 0)dev_err(dev, "(*)Error in getting Codec-3034 Interrupt gpio\n");/* 默认偏差电压 */cod3034x->mic_bias1_voltage = MIC_BIAS1_VO_3_0V;cod3034x->mic_bias2_voltage = MIC_BIAS2_VO_3_0V;cod3034x->mic_bias_ldo_voltage = MIC_BIAS_LDO_VO_3_3V;ret = of_property_read_u32(dev->of_node,"mic-bias1-voltage", &bias_v_conf);if ((!ret) && ((bias_v_conf >= MIC_BIAS1_VO_2_8V) &&(bias_v_conf <= MIC_BIAS1_VO_3_0V)))cod3034x->mic_bias1_voltage = bias_v_conf;elsedev_dbg(dev, "Property 'mic-bias1-voltage' %s",ret ? "not found" : "invalid value (use:1-3)");ret = of_property_read_u32(dev->of_node,"mic-bias2-voltage", &bias_v_conf);if ((!ret) && ((bias_v_conf >= MIC_BIAS2_VO_2_8V) &&(bias_v_conf <= MIC_BIAS2_VO_3_0V)))cod3034x->mic_bias2_voltage = bias_v_conf;elsedev_dbg(dev, "Property 'mic-bias2-voltage' %s",ret ? "not found" : "invalid value (use:1-3)");ret = of_property_read_u32(dev->of_node, "mic-adc-range", &mic_range);//从dts中获取mic的adc阈值,根据此值去检测判断mic是否存在if (!ret)cod3034x->mic_adc_range = mic_range;elsecod3034x->mic_adc_range = 1120;ret = of_property_read_u32(dev->of_node, "mic-det-delay", &mic_delay);if (!ret)cod3034x->mic_det_delay = mic_delay;elsecod3034x->mic_det_delay = 150;ret = of_property_read_u32(dev->of_node,"btn-release-value", &btn_rel_val);/*获取按键释放的阈值,用于判断按键是否释放*/if (!ret)cod3034x->btn_release_value = btn_rel_val;elsecod3034x->btn_release_value = 1100;/*若设备树中无定义,使用该值*/ret = of_property_read_u32(dev->of_node,"mic-bias-ldo-voltage", &bias_v_conf);if ((!ret) && ((bias_v_conf >= MIC_BIAS_LDO_VO_2_8V) &&(bias_v_conf <= MIC_BIAS_LDO_VO_3_3V)))cod3034x->mic_bias_ldo_voltage = bias_v_conf;elsedev_dbg(dev, "Property 'mic-bias-ldo-voltage' %s",ret ? "not found" : "invalid value (use:1-3)");dev_dbg(dev, "Bias voltage values: bias1 = %d, bias2= %d, ldo = %d\n",cod3034x->mic_bias1_voltage,cod3034x->mic_bias2_voltage,cod3034x->mic_bias_ldo_voltage);if (of_find_property(dev->of_node,"update-firmware", NULL))cod3034x->update_fw = true;elsecod3034x->update_fw = false;if (of_find_property(dev->of_node, "use-btn-adc-mode", NULL) != NULL)cod3034x->use_btn_adc_mode = true;dev_err(dev, "Using %s for button detection\n",cod3034x->use_btn_adc_mode ? "GPADC" : "internal h/w");if (cod3034x->use_btn_adc_mode) {/*以下获取按键的adc范围值,用于判断耳机按键类型,确定按键功能和上报节点*/for (i = 0; i < 4; i++) {if (of_parse_phandle_with_args(dev->of_node,"but-zones-list","#list-but-cells", i, &args))break;cod3034x->jack_buttons_zones[i].code = args.args[0];cod3034x->jack_buttons_zones[i].adc_low = args.args[1];cod3034x->jack_buttons_zones[i].adc_high = args.args[2];}/* initialize button status */cod3034x->jack_det.privious_button_state = BUTTON_RELEASE;for (i = 0; i < 4; i++)dev_err(dev, "[DEBUG]: buttons: code(%d), low(%d), high(%d)\n",cod3034x->jack_buttons_zones[i].code,cod3034x->jack_buttons_zones[i].adc_low,cod3034x->jack_buttons_zones[i].adc_high);}
}

上述就是三星平台音频codec模块的主要代码分析。

三、总结:
1、拿到BUG,首先进行问题的分析,从测试提供的资料中获取与BUG有关的所有信息,与测试人员进行沟通,确定测试流程和测试环境,避免自测产生不必要的麻烦。
2、进行问题解析,对问题进行定位,列出可能产生此现象的问题点,先排除自身模块的问题再去考虑其他模块的问题。环环相扣,层层排除。
3、保持良好的心态,认真解BUG。

音频之耳机(Exynos7872)相关推荐

  1. 支持Apple Music无损音频的耳机型号以及iPhone、iPad

    苹果公司声称,所有苹果音乐用户都可以使用"苹果无损音乐"获得更好的音频,但是要获得全部收益,需要软件和硬件.就是您需要的AirPods,耳机或其他硬件. Apple Music将在 ...

  2. android安卓切换音频声道-耳机-外放-蓝牙-实用功能系列

    切换音频声道-耳机-外放-蓝牙 功能简介 实现步骤 java代码 Android技术生活交流 完整代码ZIP:下载 更多其他页面-自定义View-实用功能合集:点击查看 功能简介 实现切换音频通道 蓝 ...

  3. 音频之耳机按键事件上报流程(Exynos7872)

    (本文仅用于本人学习记录,仅供参考) 耳机按键检测到动作后事件上报,涉及到输入子系统. Input_report_key(cod3034x->input,jd->button,1) //向 ...

  4. Android播放音频到耳机,Android音乐播放模式切换-外放、听筒、耳机

    初始化音频管理器 /** * 初始化音频管理器 */ private void initAudioManager() { audioManager = (AudioManager) context.g ...

  5. 相位测试音频mp3_苹果AirPods MAX耳机音质一大短板在哪里?蓝牙AAC编码品质讨论与测试 「Soomal」...

    AirPods Max是苹果发布不久的4399元售价定为比较高端的一款耳机,我们没有订购,因为订购后也得14周以后收货--太久了.我们今天发这篇文章是不是要云评测了?这里我们要重新复习一下蓝牙中的AA ...

  6. 创新突破,索爱S5耳机实现空间音频和动态头部跟踪

    自苹果2016年发布第一代AirPods耳机引爆TWS耳机市场,越来越多的厂商跟进推出自家的TWS耳机.时至2022年,我爱音频网累计拆解了700款音频产品,功能覆盖左右耳无缝切换.ENC通话降噪.A ...

  7. 树莓派(Linux)音频输出接口(HDMI和3.5mm耳机接口)相互切换方法

    本文主要介绍了树莓派音频输出的接口(包括HDMI音频输出和3.5 耳机接口的音频输出)音频输出接口的切换方法.音频的输出接口切换主要分为: 目录 一.系统中多媒体播放(如:网站上的音乐或者视频) 1. ...

  8. android音频调制通讯,android音频口通信(一)——2FSK信号调制

    转载请注明文章出处和作者! 作者:大熊(Xandy) 一.前言 之前一直都在博客园混(地址:http://www.cnblogs.com/xl19862005),最近才搬家至CSDN,由于前几个月刚换 ...

  9. 户外耳机品牌哪个好、最新的户外耳机品牌排行

    随着耳机的进一步发展,耳机的类别也变得广泛,比如运动耳机就成为了一个新的细分耳机品类.那么,你对它有几分认识呢?实际上,运动耳机会根据运动人士的需求,带来针对性设计,使得耳机具备使用更安全,久戴更稳固 ...

最新文章

  1. Oracle Database 快捷版 安装 连接
  2. 视觉编码(Visual Encoding)
  3. 中国移动2016年Web应用防火墙集采:绿盟、深信服中标
  4. linux下,.ko,.o , .so , .a ,.la
  5. 苹果员工出逃现象严重:人才挽留成大难题
  6. 获取数据库时间相差8小时_JAVA 程序展示时间与数据表保存的时间相差了13个小时...
  7. 前端:JS/26/实例:随机显示小星星
  8. 在AWS RDS SQL Server中恢复数据
  9. c# 串口发送接收数据
  10. c编码实现连接redis服务器
  11. oppo包名_常用APP(apk)对应包名总结(持续更新...)
  12. 秩和检验-matlab函数ranksum用法详解
  13. 微信开发-JS接口微信定位
  14. 使用Apple第三方登录Java版
  15. 流体动力学模拟软件Realflow教程,Realflow水花飞溅特效/粒子特效等特效入门教程
  16. js获取传统节假日_js判断节假日的代码分享
  17. 物联网案例_讨论物联网采用的商业案例
  18. Jmeter性能测试报告导出
  19. 开正式发票到底谁交税
  20. Android中的验证码输入框

热门文章

  1. 因数(factor)
  2. java使用geotools读取shp文件
  3. 风控知识点一:常用模型介绍
  4. 数字抽奖小程序_发挥想象力,用 PowerBI 做抽奖小程序
  5. gitgithub快速入门
  6. 一番星大厂面试经验分享第一弹
  7. java毕业设计——基于java+eclipse+sqlserver的银行帐目管理系统设计与实现(毕业论文+程序源码)——银行帐目管理系统
  8. Centos-7使用的性能监控工具
  9. 小白重装系统教程_小白一键重装系统win10教程
  10. 用半年的时间面试自己