MSM8996 Mainlining

From postmarketOS
Jump to navigation Jump to search
Note This page is WIP.

MSM8996/APQ8096 (Snapdragon 820/820E), as well as MSM8996SG/APQ8096SG (Snapdragon 821, also known as MSM8996 Pro) are Qualcomm SoCs with good support in the mainline Linux kernel. Getting it to boot on them generally does not require a lot of effort.


  • CPU: SMP (bring up secondary CPU cores), CPU frequency scaling, CPU idle
  • Storage: eMMC, SD cards, UFS, ...
  • Video: Hardware-accelerated video codec
  • Modem: Calls, SMS, Mobile data
SoC Arch Year CPU UART Storage USB Display GPU Pinctrl I²C Audio Video Thermal Modem Camera
Qualcomm MSM8996 aarch64 2016 P Y P Y Y Y Y Y Y Y Y P

First Steps


SoC Common Kernel

This is a close to mainline fork which is used as a staging area for new devices and code. It has additional patches including fixes for some as well as new drivers that aren't ready for upstream, or haven't been merged there yet. Start off by cloning this kernel tree:

git clone


Downstream DTS

This will probably be the most useful reference material. You should be able to find it in the downstream kernel somewhere in arch/arm/boot/dts/qcom, or arch/arm64/boot/dts/qcom which should be symlinked to the previous path. A DTB can also be extracted from the device in runtime from /sys/firmware/fdt, then decompiled with DTC:

dtc -I dtb -O dts fdt > fdt.dts

This however will be stripped of any comments or labels, so it makes finding things a bit harder, but you can be certain that this is the correct DTS for the device variant you have.


Having board schematics will help confirm connections between components, and especially regulators which can sometimes not be clear in downstream DTS. It can also help find UART pads or ports.

Downstream Kernel

This will help when writing drivers. The downstream drivers can be used as reference.

DTS of Other Mainlined Devices

There's a good chance that your device shares some hardware with other mainlined devices. Usually that's the case for WiFi/Bluetooth and Audio. It can help to take a look in device trees of other mainlined devices to see how things are done, or even copy similar parts.

Initial Device Tree

Every device requires a device tree that describes its hardware. Linux reads the device tree to determine what hardware is available, how it's connected, and what drivers are needed to operate it. Device trees in the kernel source tree can be found in arch/*/boot/dts. For MSM8996 devices, device trees are placed in arch/arm64/boot/dts/qcom.

Creating the DTS

Create a new file there named msm8996-<vendor>-<codename>.dts using this template:

// SPDX-License-Identifier: Your-License
 * Copyright (c) YYYY, Your Name <>

#include "msm8996.dtsi"
#include "pm8994.dtsi"
#include "pmi8994.dtsi"

/ {
	model = "Device Name";
	compatible = "vendor,codename", "qcom,msm8996";

Then add it to the Makefile in the same directory, respecting alphabetical order:

dtb-$(CONFIG_ARCH_QCOM)	+= msm8996-vendor-codename.dtb

Adding qcom,*-id

The bootloader on devices with Qualcomm SoCs uses a set of device tree properties to pick a suitable DTB for the device variant it's running on:

  • qcom,msm-id
  • qcom,pmic-id
  • qcom,board-id

This allows vendors to pack multiple DTBs for several variants of their device into a single image and use it for all of them, instead of having to create an image for each variant. Setting those properties is necessary to make the bootloader pick our device tree. You should be able to find those in the downstream DTS. Once you find them, put them in the root node:

/ {
	model = "Device Name";
	compatible = "vendor,codename", "qcom,msm8996";
	qcom,msm-id = <0x131 0x10000>;
	qcom,pmic-id = <0x20009 0x10013 0 0>;
	qcom,board-id = <34 0>;

This device tree should be enough to boot, but so far there is no way of getting any sort of feedback.

Getting Kernel Output

Getting any kind of output from the kernel is necessary to confirm that it boots, and to know if anything is not working as expected. There are a few methods to get kernel logs:

Simple Framebuffer

Framebuffer console with simplefb on Xiaomi Mi Note 2

Perhaps the easiest one to get working, provided you have suitable hardware and a nice bootloader.

It's a fbdev driver that makes use of the framebuffer configured by the bootloader for boot splash. We can use it to draw a console on the screen with little effort. All it takes is to know the memory region where the bootloader configures the framebuffer, and fortunately that should be the same across all MSM8996 devices. Unfortunately however, it usually only works on video-mode panels and doesn't work on command-mode panels. To find out what type of panel you have, check the downstream cmdline (on Android or TWRP for example). This example is taken from a Xiaomi Mi Note 2:

# cat /proc/cmdline
sched_enable_hmp=1 sched_enable_power_aware=1 app_setting.use_32bit_app_setting_pro=1 kpti=1 androidboot.hardware=qcom ehci-hcd.park=3 lpm_levels.sleep_disabled=1 cma=32M@0-0xffffffff loop.max_part=7 buildvariant=userdebug androidboot.bootdevice=624000.ufshc androidboot.verifiedbootstate=orange androidboot.veritymode=eio androidboot.keymaster=1 androidboot.serialno=91000201 androidboot.secureboot=1 androidboot.hwversion=4.5.0 androidboot.baseband=msm mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_lgd_sw43101_fhd_video:1:none:cfg:single_dsi fpsimd.fpsimd_settings=0 app_setting.use_app_setting=0

This argument is what we're looking for:


In this case the panel name is qcom,mdss_dsi_lgd_sw43101_fhd_video. What's important here is video at the end of the name. This tells us that this panel is video-mode. Command-mode panels would have cmd in the panel name.

Once you confirm that you have a suitable panel, you can proceed with enabling simplefb. You just have to add these nodes to the root node of your device tree, replacing height and width with the dimensions of your panel:

/ {
	chosen {
		#address-cells = <2>;
		#size-cells = <2>;

		framebuffer0: framebuffer@83401000 {
			compatible = "simple-framebuffer";
			reg = <0x00 0x83401000 0x00 (height * width * 3)>;
			width = <width>;
			height = <height>;
			stride = <(height * 3)>;
			format = "r8g8b8";

	reserved-memory {
		cont_splash_mem: memory@83401000 {
			reg = <0x0 0x83401000 0x0 (height * width * 3)>;

Once done, build and flash/boot the kernel. If all went well, you should get some penguins on your screen!


TODO Add info about enabling UART output