image

背景

  • Platform: RK3399
  • Kernel: v4.4.10

项目需要添加一个控制外部电路Mute的接口,主要是控制一个GPIO

过程

DTS配置

添加GPIO配置
类似:

amp-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>;
...
&pinctrl {
	...
	xxx_gpios: xxx-gpios {
		rockchip,pins = <3 16 RK_FUNC_GPIO &pcfg_pull_none>, 
				<1 17 RK_FUNC_GPIO &pcfg_pull_none>;
	}
	...
}

驱动源码添加

  1. 在驱动probe函数中获取gpio相关信息:
    xxx->amp_gpio = of_get_named_gpio(np, "amp-gpios", 0);
    if (gpio_is_valid(xxx->amp_gpio)) {
    	ret = devm_gpio_request_one(&i2c->dev, xxx->amp_gpio,GPIOF_OUT_INIT_LOW, "xxx amp ctl");
    	if (ret != 0)
    		return ret;
    }
  2. snd_kcontrol_new中添加dapm kcontrol:
    SOC_SINGLE_BOOL_EXT("Local AMP Hw Mute", 1, local_amp_hw_mute_get, local_amp_hw_mute_put),
    相关dapm kcontrol定义可查看文件:kernel/include/sound/soc.h
  3. 添加对应的xhandler_getxhandler_put函数,来控制对应的GPIO
    static int local_amp_hw_mute_get(struct snd_kcontrol *kcontrol,
    				struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
    	struct xxx_priv *xxx = snd_soc_codec_get_drvdata(codec);
    
    	ucontrol->value.integer.value[0] = xxx->amp_mute_en;
    
    	return 0;
    }
    
    static int local_amp_hw_mute_put(struct snd_kcontrol *kcontrol,
    				struct snd_ctl_elem_value *ucontrol)
    {
    	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
    	struct xxx_priv *xxx = snd_soc_codec_get_drvdata(codec);
    	
    	unsigned int val = ucontrol->value.integer.value[0];
    
    	if (val > 1)
    		return -EINVAL;
    
    	mutex_lock(&xxx->lock);
    
    	xxx->amp_mute_en = val;
    	if (gpio_is_valid(xxx->amp_gpio)) {
    		gpio_set_value(xxx->amp_gpio, 1-val);
    	}
    
    	mutex_unlock(&xxx->lock);
    
    	return 0;
    }
    这样就可以通过标准的alsa接口去控制了,如TinymixAlsamix
    用户空间调用kcontrol:snd_ctl_ioctl
    添加其他的控制类似

    kcontrol通常用于控件的音量等控制,而dapm kcontrol相关的kcontrol则是用于widget电源管理的开关

参考及扩展

  1. Asoc dapm(一) - kcontrol