架构介绍
高通平台目前使用的是SSC和SEE架构。SSC架构用于一些老平台,如MSM8953、SDM660、QCM2150等,新平台使用的都是SEE架构。从下图来看,新架构更简洁,SEE充当了core层的重要角色,负责传送request,接收event。
SEE架构优点:
统一的事件驱动框架,适用于驱动程序和算法;
在SEE上运行的任何算法都必须签名;
简单且对称的API(传感器和服务);
简单的接口来初始化/启用/激活/停用/采样传感器;
驱动程序对如何处理客户端请求有改进的控制;
可以轻松扩展以添加新的/自定义驱动程序功能;
直接在驱动程序API中更好地进行测试;
支持异步COM总线传输;
sensor移植
驱动的集成及配置
本文主要介绍在SEE架构下点亮一个sensor,下面以添加地磁AK09918C为例进行详细介绍。
如果BP侧ADSP下已有驱动,则只需要修改AP侧对应的json文件中的配置即可;如果没有相关曲中,就需要手动添加相关驱动和编译选项,再修改AP侧配置。
sensor驱动的添加路径为:
adsp_proc/ssc/sensors/ak0991x
sensor型号的配置路径为:
adsp_proc/ssc/chipset/nicobar/por.py
把要添加的sensor型号添加到系统原生的代码中,型号是驱动代码下面build/目录,有一个sns_ak0991x.scons,这个就是驱动编译相关文件,取前面sns_ak0991x就是sensor型号,其他sensor同理。型号添加如下:
--- a/adsp_proc/ssc/chipset/nicobar/por.py
+++ b/adsp_proc/ssc/chipset/nicobar/por.py
@@ -31,7 +31,7 @@ def generate(env):
- include_sensor_vendor_libs.extend(['lsm6dso', 'sns_stk3x1x', 'sns_mmc36xx', 'sns_stk3x3x', 'sns_icm4x6xx'])
+ include_sensor_vendor_libs.extend(['lsm6dso', 'sns_stk3x1x', 'sns_mmc36xx', 'sns_stk3x3x', 'sns_icm4x6xx', 'sns_ak0991x'])
env.Replace(SSC_INCLUDE_SENS_VEND_LIBS=include_sensor_vendor_libs)
env.Replace(SSC_EXCLUDE_LIBS=exclude_libs)
以上这些修改部分完成之后,就可以进行编译验证,想要确定编译是否生效:
驱动build目录下会生成sensor_img文件,内含lib库文件以及sensor驱动对应的.o文件;
查看sns_static_sensors.c文件中是否有添加的sensor型号,如果有说明参加编译了,文件路径如下:
adsp_proc/ssc/framework/src/sns_static_sensors.c
sensor驱动部分最终打包到NON_HLOS.bin镜像中,验证时可单独替换。
adb root
adb reboot bootloader
fastboot flash modem NON_HLOS.bin
json文件的修改及配置
sensor的json文件包含相关属性配置,具体添加路径为:
vendor/qcom/proprietary/sensors-see/registry/config/trinket/
路径由vendor/qcom/proprietary/sensors-see/registry/Android.mk中的TARGET_BOARD_PLATFORM决定。
sensor的json文件一般厂商会提供,需根据原理图确认相关参数取值,主要参数说明如下:
配置项 | 作用 | 备注 |
hw_platform | 平台名称 | cat /sys/devices/soc0/hw_platform |
soc_id | 平台ID | cat /sys/devices/soc0/soc_id |
is_dri | 数据上报方式 | 0:Polling 轮询方式 |
1:DRI 中断方式 | ||
hw_id | Sensor ID | 区分同型号的多个sensor |
res_idx | 分辨率 | 选择默认分辨率和范围 |
sync_stream | 同步数据流 | 是否支持同步数据流,比如S4S |
bus_type | 连接方式 | 0:I2C |
1:SPI | ||
2:UART | ||
3:I3C | ||
bus_instance | 总线控制器 | 连接总线对应的QPU端口 |
slave_config | I2C地址 | 具体Sensor的I2C地址 |
min_bus_speed_khz | 总线时钟频率 | 最小总线时钟频率 |
max_bus_speed_khz | 总线时钟频率 | 最大总线时钟频率 |
dri_irq_num | 中断号 | 具体sensor使用的GPIO号 |
irq_pull_type | 中断上/下拉方式 | 0:No Pull |
1:Pull Down | ||
2:Keeper | ||
3:Pull Up | ||
irq_is_chip_pin | 是否用作中断 | 1:MSM GPIO用作中断 |
irq_trigger_type | 中断触发方式 | 0:Rising edge |
1:Falling edge | ||
2:Rising & Falling edge | ||
3:Level triggered: High | ||
4:Level triggered: Low | ||
num_rail | power rails个数 | Sensor的供电LDO个数(VDD和VDDIO) |
rail_on_state | 供电状态 | 1:Low Power mode |
2:Normal Power mode | ||
vddio_rail | 电源轨 | /pmic/client/sensor_vddio(默认) |
json文件可以直接push到设备内部,然后重启,实现快速更新,具体步骤如下:
adb shell “rm -rf /vendor/etc/sensors/config/nicobar_ak991x_0.json” //删除之前的注册表文件
adb shell “rm -rf /mnt/vendor/persist/sensors/registry/registry/*” //删除已生成的sensor注册文件
adb push nicobar_ak991x_0.json /vendor/etc/sensors/config //push新的json文件到设备
ls –l /mnt/vendor/persist/sensors/registry/registry //重启设备后查看更新后的json文件是否注册成功
供电配置介绍
sensor通常有两路供电1.8V和2.8V,1.8V给I2C总线供电,2.8V给器件供电。
LDO供电
LDO供电配置路径:
adsp_proc/core/settings/pmic/pm/config/adsp/nicobar/pm_config_pam.c
如图,需要根据实际的LDO编号进行增加和修改,此时先查看原生代码配置,在使用过程中主要依赖于PMIC_NPA_CLIENT_NODE_SENSOR_VDDIO所在的数组,若实际设计与原生不一致,就需要修改或增减。
修改sensor供电为长供电,以L12A为例,修改如下:
--- a/rpm_proc/core/pmic/pm/config/sdm_nicobar/pm_config_target.c
+++ b/rpm_proc/core/pmic/pm/config/sdm_nicobar/pm_config_target.c
@@ -57,7 +57,7 @@ pm_rpm_ldo_rail_info_type ldo_rail_a[] =
- {0, 24, 0, PM_ACCESS_ALLOWED, PM_NONE, PM_NPA_VREG_MODE_LPM, PM_NPA_BYPASS_DISALLOWED, PM_DROOP_DETECT_DIS, 1800, 1996, 0, PM_SETTLING_ERR_EN, PM_SETTLING_EN, 0}, // LDO12 LDO510_LVP300 camera
+ {0, 24, 0, PM_ACCESS_ALLOWED, PM_ALWAYS_ON, PM_NPA_VREG_MODE_LPM, PM_NPA_BYPASS_DISALLOWED, PM_DROOP_DETECT_DIS, 1800, 1996, 0, PM_SETTLING_ERR_EN, PM_SETTLING_EN, 0}, // LDO12 LDO510_LVP300 camera
使用外挂的PM8008芯片的LDO6供电,修改如下:
--- a/kernel/msm-4.14/arch/arm64/boot/dts/qcom/sc138-evk/trinket-camera-sensor-idp.dtsi
+++ b/kernel/msm-4.14/arch/arm64/boot/dts/qcom/sc138-evk/trinket-camera-sensor-idp.dtsi
@@ -271,6 +271,7 @@
cam_vio-supply = <&L12A>;
cam_vana-supply = <&L4P>;
cam_vdig-supply = <&L1P>;
+ light_sensor_vdd = <&L6P>;
cam_clk-supply = <&camss_top_gdsc>;
--- a/kernel/msm-4.14/arch/arm64/boot/dts/qcom/sc138-evk/trinket.dtsi
+++ b/kernel/msm-4.14/arch/arm64/boot/dts/qcom/sc138-evk/trinket.dtsi
@@ -3141,6 +3141,9 @@
&L6P {
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ regulator-state-mem {
+ regulator-off-in-suspend; };
};
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -1426,6 +1426,7 @@ static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl)
static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev)
{
int32_t rc = 0;
+ struct regulator *sensor_vdd;
struct msm_sensor_ctrl_t *s_ctrl = NULL;
/* Create sensor control structure */
@@ -1463,6 +1464,14 @@ static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev)
/* Fill device in power info */
s_ctrl->sensordata->power_info.dev = &pdev->dev;
+
+ sensor_vdd = regulator_get(&pdev->dev, "light_sensor_vdd");
+ if(!sensor_vdd){
+ pr_err("sensor_vdd get failed\n");
+ }
+ regulator_set_voltage(sensor_vdd, 3000000, 3000000);
+ regulator_enable(sensor_vdd);
+
return rc;
gpio供电
sensor的供电如果由普通的gpio控制,可以在abl阶段把gpio拉高即可,修改如下:
--- a/bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.inf
+++ b/bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.inf
@@ -110,6 +110,7 @@
gEfiResetReasonProtocolGuid
gQcomRngProtocolGuid
gQcomDisplayUtilsProtocolGuid
+ gEfiTLMMProtocolGuid
--- a/bootable/bootloader/edk2/QcomModulePkg/Library/BootLib/UpdateCmdLine.c
+++ b/bootable/bootloader/edk2/QcomModulePkg/Library/BootLib/UpdateCmdLine.c
@@ -40,6 +40,7 @@
#include <Protocol/Print2.h>
+#include <Protocol/EFITlmm.h>
#include "AutoGen.h"
#include <DeviceInfo.h>
@@ -81,6 +82,39 @@ STATIC CONST CHAR8 *AndroidBootFstabSuffix =
STATIC CHAR8 *FstabSuffixEmmc = "emmc";
STATIC CHAR8 *FstabSuffixDefault = "default";
+#define SEN_VDD_2V8 27
+
+EFI_STATUS
+SetGpioStateProtocol (UINT32 GpioNum, UINT32 GpioState)
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ EFI_TLMM_PROTOCOL *TLMMProtocol = NULL;
+
+ Status = gBS->LocateProtocol (&gEfiTLMMProtocolGuid, NULL, (VOID **)&TLMMProtocol);
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((EFI_D_VERBOSE, "TLMM Protocol is not available.\n"));
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Error finding TLMM protocol: %r\n", Status));
+ return Status;
+ }
+
+ if(TLMMProtocol){
+ Status = TLMMProtocol ->ConfigGpio( (UINT32) EFI_GPIO_CFG(GpioNum,0, GPIO_OUTPUT,GPIO_NO_PULL,GPIO_2MA),TLMM_GPIO_ENABLE);
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG ((EFI_D_ERROR, "ConfigGpio: Unable to Configure MSM GPIO %d!!\n", GpioNum));
+ }
+
+ if (EFI_SUCCESS != TLMMProtocol->GpioOut((UINT32)EFI_GPIO_CFG(GpioNum, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), GpioState?GPIO_HIGH_VALUE:GPIO_LOW_VALUE))
+ DEBUG((EFI_D_ERROR, "pull up gpio SEN_VDD_2V8 Failed!\n"));
+ else
+ DEBUG((EFI_D_ERROR, "pull up gpio SEN_VDD_2V8 successfully!\n"));
+ }
+
+ return Status;
+}
+
EFI_STATUS
TargetPauseForBatteryCharge (BOOLEAN *BatteryStatus)
{
@@ -583,6 +617,12 @@ UpdateCmdLine (CONST CHAR8 *CmdLine,
UINT32 LEVerityCmdLineLen = 0;
CHAR8 RootDevStr[BOOT_DEV_NAME_SIZE_MAX];
+ Status = SetGpioStateProtocol(SEN_VDD_2V8, 1);
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "Error SetGpioStateProtocol num: %x\n", Status));
+ return Status;
+ }
+
Status = BoardSerialNum (StrSerialNum, sizeof (StrSerialNum));
--- a/bootable/bootloader/edk2/QcomModulePkg/QcomModulePkg.dec
+++ b/bootable/bootloader/edk2/QcomModulePkg/QcomModulePkg.dec
@@ -95,6 +95,7 @@
gEfiPlatformInfoProtocolGuid = { 0x157a5c45, 0x21b2, 0x43c5, { 0xba, 0x7c, 0x82, 0x2f, 0xee, 0x5f, 0xe5, 0x99 } }
# Sdcc Card Info
gEfiMemCardInfoProtocolGuid = { 0x85c1f7d2, 0xbce6, 0x4f31, { 0x8f, 0x4d, 0xd3, 0x7e, 0x03, 0xd0, 0x5e, 0xaa } }
+ gEfiTLMMProtocolGuid = { 0xad9aec18, 0x7bf0, 0x4809, { 0x9e, 0x96, 0x30, 0x12, 0x30, 0x9f, 0x3d, 0xf7 } }
调试方法
QXDM简易操作
用QXDM接收sensor模块log:
View—Common—Filtered View:新增log查看窗口;
Tools—CFG File Generator:对新增窗口进行log信息筛选过滤;
设置Filtered View用于接收sensor log,Tools->CFG File Generator,如下图;
log抓取
sensor的调试原理是利用QXDM发送命令使adsp子系统重启,再用QXDM来接收过滤出sensor从初始化、注册到正常工作的全部log,具体操作步骤如下:
修改adsp子系统重启等级,需在cmd窗口执行,指令如下:
echo related > /sys/bus/msm_subsys/devices/subsys1/restart_level //修改adsp子系统restart_level
参考上面步骤使用QXDM抓log,usb连接上设备后,在QXDM中发送command,使adsp子系统重启,指令如下:
send_data 75 37 03 48 00
以上操作就可以使QXDM过滤出SNS从初始化到启动的全部log,使用CTRL+A、Alt+M打开搜索框,可搜索所加驱动信息。
调试成功判断
指令dumpsys sensorservice
可以检测sensor hal层中哪些sensor已经init成功。
QsensorTest APP获取
Android系统本身有内置QsensorTest 这一APP,通过此APP可以获取到可用sensor,能看到调试对应的sensor型号,即表明调试成功,点击对应型号可以让sensor开始运行,点击图中的“X”停止运行。