RK3288_Android7.1在HAL层audio音频通路小结

这里先说下rk3288 HDMIin音频通路:
HDMIin:
HDMIIn声音直接通过codec输出到喇叭、耳机,不需要送到主控进行处理:
TC358749XBG-> alc5651 i2s2 -> alc5651 dac -> hp/lineout

alsa HAL 层
RK平台 android 5.1 BOX MID的SDK之后统一使用这个目录下面的代码
\hardware\rockchip\audio\tinyalsa_hal

嵌入式设备的音频系统可以被划分为板载硬件(Machine)、Soc(Platform)、Codec 三大部分

1、音频编解码器 Codec 负责处理音频信息,包括 ADC,DAC,Mixer,DSP,输入输出以及音量控制等所有
与音频相关的功能。
Codec 与处理器之间通过 I2C 总线和数字音频接口 DAI 进行通信。
I2C 总线 - 实现对 Codec 寄存器数据的读写。

2、DAI –(digital audio interface)实现音频数据在 CPU 和 Codec 间的通信,包括 I2S、PCM 和 AC97
等。
蓝牙立体声音乐播放走的是蓝牙跟 CPU 直接的 UART 接口。
在只有一组 I2S 的主控上面,蓝牙通话 SCO 暂时是通过 Codec 中继,即通过 codec 来路由选择是
否走通话,走的是 I2S 接口 。如果主控有两组 I2S,那么可以将 BT PCM 直接接到主控的 PCM 接
口上。
S/DIF(Sony/Philips Digital Interface Format),通过光纤或同轴电缆传输音频,保证音频质量。

Codec 内部通路举例 RK616

同时音频驱动有三部分:
Machine driver
sound/soc/rockchip/rk_rk616.c
其中的 Machine 驱动负责 Platform 和 Codec 之间的耦合以及部分和设备或板子特定的代码采样率
时钟配置

Platform driver
sound/soc/ rockchip /rk30_i2s.c
I2S 控制器驱动 采样率时钟 DMA 等配置

Codec driver
sound/soc/codecs/rk616_codec.c
codec 的寄存器通路的配置

android audio route通路介绍
android定义了很多的音频devices:在frameworks/base/media/java/android/media/AudioSystem.java文件中

349     // output devices, be sure to update AudioManager.java also
350     public static final int DEVICE_OUT_EARPIECE = 0x1;
351     public static final int DEVICE_OUT_SPEAKER = 0x2;
352     public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
353     public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
354     public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
355     public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
356     public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
357     public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
358     public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
359     public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
360     public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
361     public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL;
362     public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
363     public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
364     public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
365     public static final int DEVICE_OUT_USB_DEVICE = 0x4000;
366     public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
367     public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000;
368     public static final int DEVICE_OUT_LINE = 0x20000;
369     public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
370     public static final int DEVICE_OUT_SPDIF = 0x80000;
371     public static final int DEVICE_OUT_FM = 0x100000;
372     public static final int DEVICE_OUT_AUX_LINE = 0x200000;
373     public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000;
374     public static final int DEVICE_OUT_IP = 0x800000;
375     public static final int DEVICE_OUT_BUS = 0x1000000;

HAL层通过一定转换跟android上层对应
在hardware/rockchip/audio/tinyalsa_hal/alsa_route.c 中通过route_set_controls(route)中通过 get_route_config(int route)将以上的各个route值转换为codec_config中的xxx.h配置文件。

文件:./hardware/rockchip/audio/tinyalsa_hal/alsa_route.c
282 const struct config_route *get_route_config(unsigned route)  //即在这里获取音频路由设备
283 {
284     ALOGV("get_route_config() route %d", route);
285
286
287     if (!route_table) {
288         ALOGE("get_route_config() route_table is NULL!");
289         return NULL;
290     }
291     switch (route) {
292     case SPEAKER_NORMAL_ROUTE:
293         return &(route_table->speaker_normal);
        ......

HAL层中定义的 route table,再通过route_set_controls将这些table配置,最终设置到codec的寄存器

    文件:./hardware/rockchip/audio/tinyalsa_hal/codec_config/rt5651_config.h


    1110 const struct config_route_table rt5651_config_table = {
    1111     //speaker
    1112     .speaker_normal = {
    1113         .sound_card = 0,
    1114         .devices = DEVICES_0,
    1115         .controls = rt5651_speaker_normal_controls,
    1116         .controls_count = sizeof(rt5651_speaker_normal_controls) / sizeof(struct config_control),
    1117     },
        ......
        
      48 const struct config_control rt5651_speaker_normal_controls[] = {
    
      56
      57         {
      58             .ctl_name = "DAC MIXL INF1 Switch",
      59             .int_val = {on},
      60         },
      61         {
      62             .ctl_name = "DAC MIXR INF1 Switch",
      63             .int_val = {on},
      64         },
      65         {
      66             .ctl_name = "Stereo DAC MIXL DAC L1 Switch",
      67             .int_val = {on},
      68         },
      69         {
      70             .ctl_name = "Stereo DAC MIXR DAC R1 Switch",
      71             .int_val = {on},
      72         },
      73         {
      74             .ctl_name = "OUT MIXL DAC L1 Switch",
      75             .int_val = {on},
      76         },
      77         {
      78             .ctl_name = "OUT MIXR DAC R1 Switch",
      79             .int_val = {on},
      80         },
      81

HAL层会根据sound card name选择使用哪个table,如果没有匹配默认使用default_config.h

文件:./hardware/rockchip/audio/tinyalsa_hal/alsa_route.c

 89 /**
 90  * @brief route_init
 91  *
 92  * @returns
 93  */
 94 int route_init(void)
 95 {
 96     char soundCardID[20] = "";
 97     static FILE * fp;
 98     unsigned i, config_count = sizeof(sound_card_config_list) / sizeof(struct alsa_sound_card_config);
 99     size_t read_size;
100
101     ALOGV("route_init()");
102
103     fp = fopen("/proc/asound/card0/id", "rt"); //id是第0个声卡,串口执行cat /proc/asound/card0/id可以查看                                         
104     if (!fp) {
105         ALOGE("Open sound card0 id error!");
106     } else {
107         read_size = fread(soundCardID, sizeof(char), sizeof(soundCardID), fp);
108         fclose(fp);
109
110         if (soundCardID[read_size - 1] == '\n') {
111             read_size--;
112             soundCardID[read_size] = '\0';
113         }
114
115         ALOGV("Sound card0 is %s", soundCardID);
116
117         for (i = 0; i < config_count; i++) {
118             if (!(sound_card_config_list + i) || !sound_card_config_list[i].sound_card_name ||
119                 !sound_card_config_list[i].route_table)
120                 continue;
121
122             if (strncmp(sound_card_config_list[i].sound_card_name, soundCardID, //根据sound card name选择使用哪个table
123                 read_size) == 0) {
124                 route_table = sound_card_config_list[i].route_table;
125                 ALOGD("Get route table for sound card0 %s", soundCardID);
126             }
127         }
128     }
129
130     if (!route_table) {
131         route_table = &default_config_table; //如果没有匹配默认使用default_config.h
132         ALOGD("Can not get config table for sound card0 %s, so get default config table.", soundCardID);
133     }
134
135     for (i = PCM_DEVICE0_PLAYBACK; i < PCM_MAX; i++)
136          mPcm[i] = NULL;
137
138     return 0;
139 }


HAL层声卡名称sound card name和配置表列表table的声明在 ./tinyalsa_hal/codec_config/config_list.h 文件中。Audio将获取config_route_table并根据声卡0(识别到的声卡列表的第一个)和sound_card_name的名称设置路由route。
rk3288:/ # cat /proc/asound/card0/id
realtekrt5651co

文件:./hardware/rockchip/audio/tinyalsa_hal/codec_config/config_list.h

 71 struct alsa_sound_card_config sound_card_config_list[] = {
 72     {
 73         .sound_card_name = "RKRK616",
 74         .route_table = &rk616_config_table,
 75     },
 76     {
 77         .sound_card_name = "RK29RT3224",
 78         .route_table = &rt3224_config_table,
 79     },
 80     {
 81         .sound_card_name = "rockchiprt5640c",
 82         .route_table = &rt5640_config_table,
 83     },
 84     {
 85         .sound_card_name = "rockchipes8396c",
 86         .route_table = &es8396_config_table,
 87     },
 88     {
 89         .sound_card_name = "RK29RT3261",
 90         .route_table = &rt3261_config_table,
 91     },
 92     {
 93         .sound_card_name = "RK29WM8960",
 94         .route_table = &wm8960_config_table,
 95     },
 96     {
 97         .sound_card_name = "RKRT3224",
 98         .route_table = &rt3224_config_table,
 99     },
100     {
101         .sound_card_name = "RKRT3261",
102         .route_table = &rt3261_config_table,
103     },
104     {
105         .sound_card_name = "RKWM8960",
106         .route_table = &wm8960_config_table,
107     },
108     {
109         .sound_card_name = "RKRT5616",
110         .route_table = &rt5616_config_table,
111     },
112     {
113         .sound_card_name = "realtekrt5651co",
114         .route_table = &rt5651_config_table,
115     },
        ......    

.route_table的引用对应在xxxx_config.h文件中有定义,例如.route_table = &rt5651_config_table在rt5651_config.h中。

    文件:/hardware/rockchip/audio/tinyalsa_hal/codec_config/rt5651_config.h
1143 const struct config_route_table rt5651_config_table = {
1144     //speaker
1145     .speaker_normal = {
1146         .sound_card = 0,
1147         .devices = DEVICES_0,
1148         .controls = rt5651_speaker_normal_controls,
1149         .controls_count = sizeof(rt5651_speaker_normal_controls) / sizeof(struct config_control),
1150     },
1151     .speaker_incall = {
1152         .sound_card = 0,
1153         .devices = DEVICES_0,
1154         .controls = rt5651_speaker_incall_controls,
1155         .controls_count = sizeof(rt5651_speaker_incall_controls) / sizeof(struct config_control),
1156     },
        ......

然后再在.controls当中配置ctl_name和int_val,例如rt5651_speaker_normal_controls的配置,对应在kernel层会获取到这里的设置。

const struct config_control rt5651_speaker_normal_controls[] = {
   99     {
 100         .ctl_name = "RT5651 ASRC Switch",
 101         .int_val = {1},
 102     },
 103
 104     {
 105         .ctl_name = "DAC MIXL INF1 Switch",
 106         .int_val = {on},
 107     },
 108     {
 109         .ctl_name = "DAC MIXR INF1 Switch",
 110         .int_val = {on},
 111     },
 112     {
 113         .ctl_name = "Stereo DAC MIXL DAC L1 Switch",
 114         .int_val = {on},
 115     },
 116     {
 117         .ctl_name = "Stereo DAC MIXR DAC R1 Switch",
 118         .int_val = {on},
 119     },
 120     {
 121         .ctl_name = "OUT MIXL DAC L1 Switch",
 122         .int_val = {on},
 123     },
 124     {
 125         .ctl_name = "OUT MIXR DAC R1 Switch",
 126         .int_val = {on},
 127     },

 128     {
 129         .ctl_name = "HPOVOL L Switch",
 130         .int_val = {on},
 131     },
 132     {
 133         .ctl_name = "HPOVOL R Switch",
 134         .int_val = {on},
 135     },
 136     {
 137         .ctl_name = "HPO MIX HPVOL Switch",
 138         .int_val = {on},
 139     },
 140     {
 141         .ctl_name = "HPO MIX HPVOL Switch",
 142         .int_val = {on},
 143     },
 144     {
 145         .ctl_name = "HPO L Playback Switch",
 146         .int_val = {on},
 147     },
 148     {
 149         .ctl_name = "HPO R Playback Switch",
 150         .int_val = {on},
 151     },

 156     {    .ctl_name = "LOUT MIX DAC L1 Switch",    .int_val = {on},    },
 157     {    .ctl_name = "LOUT MIX DAC R1 Switch",    .int_val = {on},    },
 158
 159     //   SPEAKER_NORMAL_ROUTE  HEADSET_NORMAL_ROUTE
 160     {    .ctl_name =   "LOUT MIX OUTVOL L Switch",    .int_val = {on},    },
 161     {    .ctl_name =   "LOUT MIX OUTVOL R Switch",    .int_val = {on},    },
 162
 163     {    .ctl_name = "LOUT L Playback Switch",    .int_val = {on},    },
 164     {    .ctl_name = "LOUT R Playback Switch",    .int_val = {on},    },
 165
 166     //{    .ctl_name = "OUT Playback Volume",    .int_val = {26, 26},    }, // 31 31
 167
 168     {    .ctl_name = "DAC1 Playback Volume",    .int_val = {128, 128},    }, // {136, 136} //这里是音量设置

};

音量值调节:
hardware/rockchip/audio/tinyalsa_hal/codec_config/rt5651_config.h

    const struct config_control rt5651_bluetooth_voip_controls[] = {

     // init IN2p/n for line in input.
     const struct config_control  rt5651_line_in_capture_controls[] = {

      // try run it first, to fix conflict
     {    .ctl_name = "RECMIXL INL1 Switch",    .int_val = {on},    }, //Replace mic: conf for L.in.L used
     {    .ctl_name = "RECMIXR INR1 Switch",    .int_val = {on},    }, //Replace mic: conf for L.in.R used
     {    .ctl_name = "RECMIXL BST2 Switch",    .int_val = {off},    }, // close conf for micL used
     {    .ctl_name = "RECMIXR BST2 Switch",    .int_val = {off},    }, // close conf for micR used
     {    .ctl_name = "RECMIXL BST1 Switch",    .int_val = {off},    },
     {    .ctl_name = "RECMIXR BST1 Switch",    .int_val = {off},    },
     {    .ctl_name = "RECMIXL BST3 Switch",    .int_val = {off},    },
     {    .ctl_name = "RECMIXR BST3 Switch",    .int_val = {off},    },
     {    .ctl_name = "Stereo1 ADC L1 Mux",    .str_val = "ADC",    },
     {    .ctl_name = "Stereo1 ADC R1 Mux",    .str_val = "ADC",    },
     {    .ctl_name = "Stereo1 ADC MIXL ADC1 Switch",    .int_val = {on},    },
     {    .ctl_name = "Stereo1 ADC MIXR ADC1 Switch",    .int_val = {on},    },
     {    .ctl_name = "ADC Capture Switch",    .int_val = {on, on},    },
     {    .ctl_name = "IN2 Boost",    .int_val = {2},    },
     {    .ctl_name = "ADC Capture Volume",    .int_val = {47, 47},    },
    // if you feel vol is low/high, can adjust it from 37 to 80. [63 is copied from rt5616]
    {    .ctl_name = "ADC Capture Volume",    .int_val = {63, 63},    }, // {57, 57} {47, 47}   //这里调节音量值设置

音频打开调用流程:
####  route_pcm_open() 
####    -> get_route_config(route)
####                -> route_set_controls()
####                    -> route_pcm_close()
####                        -> route_set_controls()
                                 ->

如何debug
1、通过看 kernel log 确认 alsa sound card 有没有注册。

rk3288_mid:/ $ dmesg | grep ALSA -EC5                                          
[    1.551902] I : [File] : drivers/gpu/arm/mali400/mali/linux/mali_kernel_linux.c; [Line] : 417; [Func] : mali_module_init(); svn_rev_string_from_arm of this mali_ko is '-cf8da18', rk_ko_ver is '5', built at '11:19:29', on 'Jan 15 2020'.
[    1.553256] Mali: Mali device driver loaded
[    1.554211] of_get_named_gpiod_flags: parsed 'gpios' property of node '/rockchip-key/power-key[0]' - status (0)
[    1.554274] of_get_named_gpiod_flags: parsed 'gpios' property of node '/rockchip-key/power-key[0]' - status (0)
[    1.554741] input: rk29-keypad as /devices/platform/rockchip-key/input/input2
[    1.556689] ALSA device list:
[    1.556778]   #0: realtek,rt5651-codec
[    1.556977]   #1: rk,hdmiin-tc358749x-codec
[    1.557280]   #2: rockchip,hdmi
[    1.557312]   #3: rockchip-cdndp-sound
[    1.565498] mmc_host mmc2: Bus speed (slot 0) = 150000000Hz (slot req 150000000Hz, actual 150000000HZ div = 0)

2、确认 route 是否正常
声卡注册好之后确认 alsa route 是否正确
通过命令 logcat -s alsa_route

rk3288_mid:/ $ logcat -s alsa_route
--------- beginning of system
--------- beginning of main
01-01 01:19:51.274   255   324 D alsa_route: route_set_controls() set route 24
01-01 01:38:30.866   255   324 D alsa_route: route_info->sound_card 0, route_info->devices 0  
01-01 01:38:30.866   255   324 D alsa_route: route_set_controls() set route 0
01-01 01:38:34.105   255   324 D alsa_route: route_set_controls() set route 24
01-01 01:38:35.655   255   324 D alsa_route: route_info->sound_card 0, route_info->devices 0  
01-01 01:38:35.656   255   324 D alsa_route: route_set_controls() set route 0
01-01 01:39:23.035   255   324 D alsa_route: route_set_controls() set route 24

上面命令跑了两个路由 route 0 route 24。route的宏定义在alsa_audio.h中。

文件:hardware/rockchip/audio$ vi tinyalsa_hal/alsa_audio.h
 62 typedef enum _AudioRoute {
 63     SPEAKER_NORMAL_ROUTE = 0,
 64     SPEAKER_INCALL_ROUTE, // 1
 65     SPEAKER_RINGTONE_ROUTE,
 66     SPEAKER_VOIP_ROUTE,
 67
 68     EARPIECE_NORMAL_ROUTE, // 4
 69     EARPIECE_INCALL_ROUTE,
 70     EARPIECE_RINGTONE_ROUTE,
 71     EARPIECE_VOIP_ROUTE,
 72
 73     HEADPHONE_NORMAL_ROUTE, // 8
 74     HEADPHONE_INCALL_ROUTE,
 75     HEADPHONE_RINGTONE_ROUTE,
 76     SPEAKER_HEADPHONE_NORMAL_ROUTE,
 77     SPEAKER_HEADPHONE_RINGTONE_ROUTE,
 78     HEADPHONE_VOIP_ROUTE,
 79
 80     HEADSET_NORMAL_ROUTE, // 14
 81     HEADSET_INCALL_ROUTE,
 82     HEADSET_RINGTONE_ROUTE,
 83     HEADSET_VOIP_ROUTE,
 84
 85     BLUETOOTH_NORMAL_ROUTE, // 18
 86     BLUETOOTH_INCALL_ROUTE,
 87     BLUETOOTH_VOIP_ROUTE,
......
}AudioRoute;
 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值