Rockchip RK3399 - ASoC 声卡之Jack设备

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板eMMC :16GBLPDDR3 :4GB显示屏 :15.6英寸HDMI接口显示屏u-boot :2023.04linux   :6.3----------------------------------------------------------------------------------------------------------------------------

jack字面上就是插孔,什么是插孔,我理解就是我们音频设备上的麦克风、耳机等设备的插孔。

那jack设备是用来实现什么功能的呢?其主要目的是为了实现耳机、麦克风等设备插入和拔出的检测。

我们以耳机检测为例来说说,一般的耳机检测包括普通的耳机检测和带有麦克风的耳机检测两种,这两种耳机统称为Headset,而对于不带麦克风的耳机,一般统称为Headphone。

对于Headset装置的插入检测,一般是通过jack来完成,大致原理是使用带检测机械结构的耳机插座,将检测引脚连接到SoC的GPIO口,如下图所示,当耳机插入时。耳机插头的金属会碰到检测引脚,使得检测引脚的电平发生改变,从而触发中断。这样就可以在中断处理函数中读取GPIO口的值,进一步判断是耳机插入还是拔出。

ALSA CORE已经实现了jack中间层,在文件include/sound/jack.h中提供了访问jack中间层代码的API。

一、ALSA核心数据结构

1.1 struct snd_jack 

Jack使用struct snd_jack数据结构来描述;

struct snd_jack {
        struct list_head kctl_list;
        struct snd_card *card;
        const char *id;
#ifdef CONFIG_SND_JACK_INPUT_DEV
        struct input_dev *input_dev;
        struct mutex input_dev_lock;
        int registered;
        int type;
        char name[100];
        unsigned int key[6];   /* Keep in sync with definitions above */
#endif /* CONFIG_SND_JACK_INPUT_DEV */
        int hw_status_cache;
        void *private_data;
        void (*private_free)(struct snd_jack *);
};

其中:

  • kctl_list:保存jack kcontrol的链表,链表中的每一个元素都是struct snd_jack_kctl;
  • card:所属声卡设备;
  • id:jack的唯一标识符;
  • input_dev:输入设备结构体(struct input_dev)指针,用于处理与jack相关的输入事件;
  • input_dev_lock:输入设备锁,用于保护对输入设备的操作;
  • registered:jack是否已经注册的标志;
  • type:jack能够上报的类型,参考enum snd_jack_types;
  • name:jack的名称字符串,长度为100字节;
  • key:如果jack能够上报SND_JACK_BTN_0、SND_JACK_BTN_1等类型,数组元素key[x]存放SND_JACK_BTN_x对应的EV_KEY事件的事件编码;
  • hw_status_cache:硬件状态的缓存值;
  • private_data:私有数据指针,可以用于存储与jack相关的其他数据;
  • private_free:私有数据释放函数指针,用于在释放snd_jack时释放相关的私有数据;
1.2 enum snd_jack_types 

jack能够上报的类型如结构体定义描述;

/**
 * enum snd_jack_types - Jack types which can be reported
 * @SND_JACK_HEADPHONE: Headphone
 * @SND_JACK_MICROPHONE: Microphone
 * @SND_JACK_HEADSET: Headset
 * @SND_JACK_LINEOUT: Line out
 * @SND_JACK_MECHANICAL: Mechanical switch
 * @SND_JACK_VIDEOOUT: Video out
 * @SND_JACK_AVOUT: AV (Audio Video) out
 * @SND_JACK_LINEIN:  Line in
 * @SND_JACK_BTN_0: Button 0
 * @SND_JACK_BTN_1: Button 1
 * @SND_JACK_BTN_2: Button 2
 * @SND_JACK_BTN_3: Button 3
 * @SND_JACK_BTN_4: Button 4
 * @SND_JACK_BTN_5: Button 5
 *
 * These values are used as a bitmask.
 *
 * Note that this must be kept in sync with the lookup table in
 * sound/core/jack.c.
 */
enum snd_jack_types {
        SND_JACK_HEADPHONE      = 0x0001,  // 如果jack->type & 0x001=0x001,则设置input设备可以上报EV_SW事件,事件编码为0x02
        SND_JACK_MICROPHONE     = 0x0002,  // 同上,设置input设备可以上报EV_SW事件,事件编码为0x04
        SND_JACK_HEADSET        = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
        SND_JACK_LINEOUT        = 0x0004,  // 同上,设置input设备可以上报EV_SW事件,事件编码为0x04
        SND_JACK_MECHANICAL     = 0x0008, /* If detected separately */ 同上,设置input设备可以上报EV_SW事件,事件编码为0x07
        SND_JACK_VIDEOOUT       = 0x0010,  // 同上,设置input设备可以上报EV_SW事件,事件编码为0x08
        SND_JACK_AVOUT          = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT,
        SND_JACK_LINEIN         = 0x0020,  // 同上,则设置input设备可以上报EV_SW事件,事件编码为0x0d

        /* Kept separate from switches to facilitate implementation */
        SND_JACK_BTN_0          = 0x4000,   // 如果jack->type & 0x4000=0x4000,则设置input设备可以上报EV_KEY事件,事件编码为0x100
        SND_JACK_BTN_1          = 0x2000,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x101
        SND_JACK_BTN_2          = 0x1000,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x102
        SND_JACK_BTN_3          = 0x0800,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x103
        SND_JACK_BTN_4          = 0x0400,   // 同上,设置input设备可以上报EV_KEY事件,事件编码为0x104
        SND_JACK_BTN_5          = 0x0200,   // 同上。设置input设备可以上报EV_KEY事件,事件编码为0x105
};
1.3 struct snd_jack_kctl

ALSA中使用struct snd_jack_kctl数据结构来描述jack中的kcontrol,定义在sound/core/jack.c:

struct snd_jack_kctl {
        struct snd_kcontrol *kctl;
        struct list_head list;  /* list of controls belong to the same jack */
        unsigned int mask_bits; /* only masked status bits are reported via kctl */
        struct snd_jack *jack;  /* pointer to struct snd_jack */
        bool sw_inject_enable;  /* allow to inject plug event via debugfs */
#ifdef CONFIG_SND_JACK_INJECTION_DEBUG
        struct dentry *jack_debugfs_root; /* jack_kctl debugfs root */
#endif
};

其中:

  • kctl:指向struct snd_kcontrol的指针;
  • list:链表节点,用于将当前节点链接到snd_jack的kctl_list链表;
  • mask_bits:当前jack control能够上报的类型,参考enum snd_jack_types;比如我们设置了mask_bits值为SND_JACK_HEADPHONE,当耳机插入或者拔出的时候HP_DET引脚就会接收到中断,中断处理程序读取引脚电平,并通过input设备上报SND_JACK_HEADPHONE对应的EV_SK事件,事件编码为0x02,值为插入/拔出;
  • jack:指向struct snd_jack的指针,表示与snd_jack_kctl相关联的jack;
  • sw_inject_enable:允许通过debugfs注入插头事件;
  • jack_debugfs_root:jack_kctl debugfs根目录;
1.4 关系图

为了更加清晰的了解struct snd_jack 、struct snd_kcontrol、struct snd_jack_kctl 等数据结构的关系,我们绘制了如下关系图:

其中我们比较关注的点是:

  • jack是挂在snd_card成员devices链表下面的一个snd_device;
  • snd_jack中的字段:kctl_list,该链表保存所有的jack kcontrol,每个jack kcontrol都关联一个kcontrol;
  • snd_jack中的字段:input_dev是一个input设备,用于上报耳机插入/拔出、麦克风插入/拔出等事件。

二、ALSA核心API

2.1 创建Jack设备

jack设备的创建可以通过snd_jack_new函数来完成,函数接收6个参数:

  • card:ALSA声卡设备;
  • id:jack设备唯一标识;
  • type:由枚举类型snd_jack_type的位掩码组成,表示该jack能够上报的类型;这里我们可以理解为jack能够检测的类型,比如耳机插入/拔出;
  • jack:jack指针的指针,用于保存分配的snd_jack;
  • initial_kctl: 如果为真,则创建一个jack kcontrol并将其添加到jack的kctl_list链表中;
  • phantom_jack:对于虚拟jack,不创建输入设备;

函数定义在sound/core/jack.c;

/**
 * snd_jack_new - Create a new jack
 * @card:  the card instance
 * @id:    an identifying string for this jack
 * @type:  a bitmask of enum snd_jack_type values that can be detected by
 *         this jack
 * @jjack: Used to provide the allocated jack object to the caller.
 * @initial_kctl: if true, create a kcontrol and add it to the jack list.
 * @phantom_jack: Don't create a input device for phantom jacks.
 *
 * Creates a new jack object.
 *
 * Return: Zero if successful, or a negative error code on failure.
 * On success @jjack will be initialised.
 */
int snd_jack_new(struct snd_card *card, const char *id, int type,
                 struct snd_jack **jjack, bool initial_kctl, bool phantom_jack)
{
        struct snd_jack *jack;
        struct snd_jack_kctl *jack_kctl = NULL;
        int err;
        static const struct snd_device_ops ops = {     // jack设备操作集
                .dev_free = snd_jack_dev_free,
#ifdef CONFIG_SND_JACK_INPUT_DEV  // 这个宏,默认是配置的
                .dev_register = snd_jack_dev_register,
                .dev_disconnect = snd_jack_dev_disconnect,
#endif /* CONFIG_SND_JACK_INPUT_DEV */
        };

        if (initial_kctl) {  // 如果为真,动态创建一个jack kcontrol ,包含一个名称为id的kcontrol
                jack_kctl = snd_jack_kctl_new(card, id, type); // jack kcontrol的mask字段设置为type
                if (!jack_kctl)
                        return -ENOMEM;
        }

        jack = kzalloc(sizeof(struct snd_jack),
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值