MSM8916 Mainlining

From postmarketOS
Jump to navigation Jump to search

MSM8916/APQ8016 (or Snapdragon 410) is Qualcomm SoC released in 2014. Support in mainline was originally added for the Dragonboard 410c, but most of it also works well on other MSM8916-based devices.


The following features provided by the MSM8916 SoC are supported in mainline and should work on most MSM8916-based devices after the device tree has been set up:

  • UART
  • USB
  • Internal/External Storage (eMMC/SD card)
  • WiFi/Bluetooth
  • GPU, Display (Note: There are many different display panels and each needs a custom panel driver...)
  • Sound (Note: Some devices use a custom speaker amplifier instead of the one provided by PM8916...)
  • Buttons
  • Modem (SMS, voice calls with audio)

Certain components (e.g. touchscreen, sensors, ...) are device-specific. With a bit of luck, some will be already supported by mainline, others won't.


The first MSM8916 devices using mainline were added in pmaports!582. The following components are involved when running mainline on MSM8916 devices.


The close-to-mainline Linux kernel fork with patches that have not been accepted/submitted upstream yet. Patches in the master branch are generally in good shape unless marked otherwise. Submit a PR to add new patches there.


The reference bootloader provided by Qualcomm for MSM8916 is open-source and based on Little Kernel (LK). Most devices will use this bootloader in a more or less modified form. On Samsung devices for example, the standard Fastboot interface was replaced by their proprietary download mode.

lk2nd is a fork of the reference bootloader with the goal to provide an unified boot interface on all MSM8916 devices. It provides a standard Fastboot interface (even on Samsung devices!). It also used for a number of mainline quirks (e.g. to set a WiFi/BT MAC address in the device tree; without lk2nd, WiFi/BT does not work out of the box). Eventually, more quirks may be added in the future. It is therefore recommended for all mainline MSM8916 devices. Porting it is generally much easier than porting mainline.

Note: lk2nd does not replace your stock bootloader. It is packed into a standard Android boot image and loaded by the stock bootloader from the boot partition. The real boot partition used by lk2nd is then placed with 1 MB offset into the boot partition.


The shared device package in postmarketOS. It contains shared dependencies and configuration files.

Getting Started


  • MSM8916-based device
  • UART if possible (with a bit of luck you may get USB working without UART)
  • (Downstream) Linux kernel source for your device (explaining ways to mainline without kernel source is out of scope for this article)
  • Basic knowledge about Linux, Git, C, Device Trees, ...
  • Willingness to learn about "mainlining" and to figure out things on your own

Before you start

Warning: Mainlining (or any unintended "modding") may brick your device permanently if you make a mistake. There is a good chance that nothing happens if you are a bit careful, but do not continue with a device that you need every day. You have been warned!

Mainlining is not easy. MSM8916 is a platform where a lot components can be easily enabled by only setting up a device tree, which can be largely copied from other devices with minor changes. However, at some point you will reach the point where you would like to enable a particular component that is device-specific (e.g. the touchscreen, sensors, ...). In this case you will be largely on your own, and need to figure out how to enable it yourself. (Do you just need to add something to the device tree or even write a new kernel driver?) That requires some familiarity with the way the Linux kernel is working for MSM8916.

The best way to make yourself familiar with the process is to attempt to figure out some simple things on your own. Therefore, this guide will only describe everything until (eventually) USB network is working. For everything else, this article provides only some information that might be helpful to figure it out yourself.

If you have any questions, ask in the mainline channel on Matrix or IRC. Make sure to mention "msm8916" if your question is specific to this article.


First, let's take a short look at the downstream kernel. The device trees on downstream are usually in arch/arm/boot/dts/qcom/msm8916-*.dts(i) (in Samsung kernels: arch/arm/boot/dts/samsung/msm8916/msm8916-*.dts(i)). You need to find the .dts file that describes the device tree for your device. One way to check this is to look at /proc/device-tree/model on downstream, and search for the same string in that directory.

Now, let's check how it looks on mainline. Clone msm8916-mainline/linux and take a look at arch/arm64/boot/dts/qcom/. This is the directory where you will add your device tree later.


Note: lk2nd is not strictly required to boot mainline. However, certain features (e.g. WiFi and BT) only work with some additional mainline quirks included in lk2nd. In the future, there may be more features that depend on extra code that needs to be run in the bootloader. Therefore, lk2nd is recommended for all devices, even if your stock bootloader already provides a standard Fastboot interface.

msm8916-mainline/lk2nd does not require any device-specific code. Normally, it should just run out of the box on your device. However, most stock bootloaders require a valid device tree to be present in the Android boot image. To avoid problems, a dummy device tree with the qcom,board-id from the downstream kernel needs to be added to lk2nd.

Before you make any changes, try building it. Then, add a new file for your device to dts/ that looks like:

// SPDX-License-Identifier: GPL-2.0-only


/include/ "msm8916.dtsi"

/ {
	// This is used by the bootloader to find the correct DTB
	qcom,msm-id = <206 0>; // qcom,msm-id = <247 0>; for APQ8016
	qcom,board-id = /* FIXME: Check your downstream device tree */;

	model = "Device Name"; // FIXME
	compatible = "<vendor>,<codename>", "qcom,msm8916", "lk2nd,device"; // FIXME

Note the FIXME lines in the example above. This is where you still need to make changes for your device. For example, replace the model with a display name for your device, and the placeholders in the compatible string.

The qcom,board-id property allows the bootloader to select the correct device tree. MSM8916 devices usually use QCDT (multiple device tree blobs (dtb) are packaged in one Android boot image). You should be able to find the qcom,board-id in the downstream .dts file for your device.

Some devices (in particular Samsung) do not implement the QCDT specification correctly - they all have the same qcom,board-id. In that case, the device cannot be detected only based on the selected DTB. For Samsung devices, msm8916-samsung-r0x.dts covers multiple devices - the actual device is then detected using androidboot.bootloader version passed by the Samsung bootloader.

Make sure to add the new file to dts/, and re-compile lk2nd.

With a bit of luck, flashing the boot image on your device should make it boot into a Fastboot screen, and the device should show up via USB. If either of that is not the case, we need to investigate further. Sometimes, there is some kind of Micro-USB_Interface_Controller that sits between USB and the SoC, which may require manual setup to work correctly. Setting that up would require device-specific code. If you need help, ask in the mainline channel on Matrix or IRC.

Device Package

Before you can build the mainline kernel for your device, you need to setup the postmarketOS device packages. Avoid creating the device/kernel package directly through pmbootstrap. There is no need to analyze a boot image. (The boot image offsets are not actually used on MSM8916; plus, all MSM8916 devices should use Fastboot through lk2nd...) You can start with the following examples:


# Reference: <>
pkgdesc="Device Name"
options="!check !archcheck"
depends="postmarketos-base mkbootimg linux-postmarketos-qcom-msm8916 soc-qcom-msm8916"

build() {
	devicepkg_build $startdir $pkgname

package() {
	devicepkg_package $startdir $pkgname


# Reference: <>
# Please use double quotes only. You can source this file in shell scripts.

deviceinfo_name="Device Name"
deviceinfo_year="<release year>"

# Device related

# MSM DRM cannot take over the framebuffer from the bootloader at the moment

# Bootloader related
deviceinfo_kernel_cmdline="earlycon console=ttyMSM0,115200 PMOS_NO_OUTPUT_REDIRECT"

Follow the postmarketOS porting guide until the device package builds successfully.

Build Kernel

Go back to your msm8916-mainline/linux clone. Try to build it with

$ cd path/to/msm8916-mainline/linux
$ source path/to/pmbootstrap/helpers/

# Initialize kernel configuration
$ make msm8916_defconfig pmos.config

# Compile kernel; replace <cores> with the number of cores you'd like to use for compilation
$ make -j<cores>

# Create postmarketOS package with your built kernel
$ pmbootstrap build --envkernel linux-postmarketos-qcom-msm8916

This is how you can build and test local changes for the kernel from now on. Now we will continue setting up an initial device tree for your device.

Initial Device Tree

The available hardware components of a device are described in the device tree (see e.g. this introduction). Components provided by the SoC (MSM8916) are described in the common include msm8916.dtsi. You need to create a device tree with the device-specific configuration, plus additional components used in your device (e.g. touchscreen).

The device tree for your device should be placed in a new file at arch/arm64/boot/dts/qcom/msm8916-<vendor>-<codename>.dts. A very simple device tree that only enables UART output could look like this:

// SPDX-License-Identifier: GPL-2.0-only


#include "msm8916.dtsi"
#include "pm8916.dtsi"

/ {
	model = "Device Name"; // FIXME
	compatible = "<vendor>,<codename>", "qcom,msm8916"; // FIXME

	aliases {
		serial0 = &blsp1_uart2;

	chosen {
		stdout-path = "serial0";

	soc {
		serial@78b0000 {
			status = "okay";
			pinctrl-names = "default", "sleep";
			pinctrl-0 = <&blsp1_uart2_default>;
			pinctrl-1 = <&blsp1_uart2_sleep>;

MSM8916 has 122 GPIO pins that can be configured with different functions. GPIO 4 and 5 can be configured to provide UART ("blsp1_uart2"). In the Linux kernel, the Pin Control Subsystem (pinctrl) handles configuring the pins with a specific function.

Therefore, the above device tree does the following:

  • aliases and chosen: Set up BLSP UART2 as default console output
  • serial@78b0000: This node is defined in msm8916.dtsi but disabled by default (status = "disabled")
    • status = "okay" Enable it
    • pinctrl: configure GPIO 4 and 5 for UART (blsp1_uart2_default and blsp1_uart2_sleep are default pin configurations that are defined in the common msm8916-pins.dtsi

Before you build, don't forget to add an entry to arch/arm64/boot/dts/qcom/Makefile. This device tree should be already enough to boot with working serial console if you are able to access UART. If you don't have UART you will need to skip testing this step and hope that USB works out of the box in the next step.

Storage & USB

Let's look at a more complex device tree now. The goal is to enable internal/external storage (eMMC) and to make USB gadget mode working so you can attach your device to a PC and get a debugging console. This is where things start to get slightly different between devices.

Update your device tree with something like the following:

// SPDX-License-Identifier: GPL-2.0-only


#include "msm8916.dtsi"
#include "pm8916.dtsi"
#include <dt-bindings/gpio/gpio.h>

/ {
	model = "Device Name"; // FIXME
	compatible = "<vendor>,<codename>", "qcom,msm8916"; // FIXME

	aliases {
		serial0 = &blsp1_uart2;

	chosen {
		stdout-path = "serial0";

	soc {
		sdhci@7824000 {
			status = "okay";

			vmmc-supply = <&pm8916_l8>;
			vqmmc-supply = <&pm8916_l5>;

			pinctrl-names = "default", "sleep";
			pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>;
			pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off>;

		sdhci@7864000 {
			status = "okay";

			vmmc-supply = <&pm8916_l11>;
			vqmmc-supply = <&pm8916_l12>;

			pinctrl-names = "default", "sleep";

			pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>; // add &sdc2_cd_on if you use cd-gpios below
			pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>; // add &sdc2_cd_off if you use cd-gpios below

			// FIXME: Either or - compare with downstream
			//cd-gpios = <&msmgpio 38 GPIO_ACTIVE_LOW>;

		serial@78b0000 {
			status = "okay";
			pinctrl-names = "default", "sleep";
			pinctrl-0 = <&blsp1_uart2_default>;
			pinctrl-1 = <&blsp1_uart2_sleep>;

		usb@78d9000 {
			status = "okay";
			dr_mode = "peripheral";


			ulpi {
				phy {
					v1p8-supply = <&pm8916_l7>;
					v3p3-supply = <&pm8916_l13>;

		 * Attempting to enable these devices causes a "synchronous
		 * external abort". Suspected cause is that the debug power
		 * domain is not enabled by default on this device.
		 * Disable these devices for now to avoid the crash.
		 * See:
		tpiu@820000 { status = "disabled"; };
		funnel@821000 { status = "disabled"; };
		replicator@824000 { status = "disabled"; };
		etf@825000 { status = "disabled"; };
		etr@826000 { status = "disabled"; };
		funnel@841000 { status = "disabled"; };
		debug@850000 { status = "disabled"; };
		debug@852000 { status = "disabled"; };
		debug@854000 { status = "disabled"; };
		debug@856000 { status = "disabled"; };
		etm@85c000 { status = "disabled"; };
		etm@85d000 { status = "disabled"; };
		etm@85e000 { status = "disabled"; };
		etm@85f000 { status = "disabled"; };

// TODO (wiki): Explain setup of dummy extcon device to make USB work

// FIXME: Verify all regulators with downstream
&smd_rpm_regulators {
	vdd_l1_l2_l3-supply = <&pm8916_s3>;
	vdd_l4_l5_l6-supply = <&pm8916_s4>;
	vdd_l7-supply = <&pm8916_s4>;

	s1 {
		regulator-min-microvolt = <500000>;
		regulator-max-microvolt = <1300000>;

	s3 {
		regulator-min-microvolt = <1200000>;
		regulator-max-microvolt = <1300000>;

	s4 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <2100000>;

	l1 {
		regulator-min-microvolt = <1225000>;
		regulator-max-microvolt = <1225000>;

	l2 {
		regulator-min-microvolt = <1200000>;
		regulator-max-microvolt = <1200000>;

	l3 {
		regulator-min-microvolt = <500000>;
		regulator-max-microvolt = <1287500>;

	l4 {
		regulator-min-microvolt = <2050000>;
		regulator-max-microvolt = <2050000>;

	l5 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <1800000>;

	l6 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <1800000>;

	l7 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <1800000>;

	l8 {
		regulator-min-microvolt = <2850000>;
		regulator-max-microvolt = <2900000>;

	l9 {
		regulator-min-microvolt = <3300000>;
		regulator-max-microvolt = <3300000>;

	l10 {
		regulator-min-microvolt = <2700000>;
		regulator-max-microvolt = <2800000>;

	l11 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <2950000>;
		regulator-system-load = <200000>;

	l12 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <2950000>;

	l13 {
		regulator-min-microvolt = <3075000>;
		regulator-max-microvolt = <3075000>;

	l14 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <3300000>;

	l15 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <3300000>;

	l16 {
		regulator-min-microvolt = <1800000>;
		regulator-max-microvolt = <3300000>;

	l17 {
		regulator-min-microvolt = <2850000>;
		regulator-max-microvolt = <2850000>;

	l18 {
		regulator-min-microvolt = <2700000>;
		regulator-max-microvolt = <2700000>;

Whoa! That's quite a lot of new stuff. Let's look at the new lines in detail:

  • sdhci@7824000: This enables the internal storage. There should not be any change needed here, it should work as-is.
  • sdhci@7864000: This is the external storage. There is a FIXME in there because there is an implementation detail that differs between devices. Some devices have their SD card slot behind the battery. In that case, they may have avoided implementing the chip detect ("cd") GPIO that is used to detect if a SD card is inserted (or removed). In any case, check if downstream defines cd-gpios, usually on the &sdhc_2 node. If yes, add it together with the other comment, otherwise put non-removable.
  • usb@78d9000: This enables the USB code and forces it to USB gadget ("peripheral") mode. (If you want, you can attempt to get OTG/Host mode working later...)
  • tpiu@820000, funnel@821000, ...: This is a workaround to avoid a boot error on production MSM8916 devices. Check the link in the comment if you are curious.
  • &smd_rpm_regulators: This block defines constraints for the regulators provided by PM8916 (the PMIC used by MSM8916). Most importantly, you define the voltage range that can be set by drivers for the regulators. Unless your device manufacturer felt very special, all MSM8916 devices should use these regulator voltages. Nevertheless it is important to check for differences in downstream. Here are some tips and tricks:
    • Downstream regulators look a bit different, but they are usually defined in msm8916-regulators.dtsi. Note that some device trees may override the regulator voltages in a device-specific .dts(i).
    • "Corner" regulators are a special type of regulators downstream that are used for some kind of voting mechanism. The idea is that different components in the SoC vote for the voltage level they currently require, and the Resource Power Manager (RPM) picks the appropriate voltage based on the votes. Mainline does not have corner regulators, so just ignore them for now.
    • s2 is special (used to control the voltage for the CPU cores) and is not touched by mainline at the moment. Just ignore it.
    • Also ignore the -ao and -so regulators (usually just on l7)
Warning: Make sure to check the regulators carefully and ask for feedback before testing the changes in the next step.

For testing, consider enabling the debug-shell hook so you can avoid flashing the rootfs as a first step. (Note that you need to remove it explicitly using hook_del when you want to try flashing the rootfs later...) With a bit of luck, the device should show up on telnet via USB after booting with these changes.

If USB is working, try flashing the rootfs to check if the internal storage is working. Your device should then boot into the full postmarketOS installation and provide SSH over the USB network interface.

TODO USB does not actually work out of the box with the instructions above (note the TODO comment in the device tree example). Please ask for help in the chat for now.

Finishing up

If the features enabled during the guide are working, then it is a good time to do some cleanup and propose your changes "upstream" on msm8916-mainline/linux. This repository is intended as a temporary place for patches that are already in good quality, but either pending or not quite finished to submit them on the Linux mainline mailing lists.

To maintain the quality, all patches are required to:

  • Compile without warnings
  • Have no criticial (ERROR:) checkpatch failures (also see Style-check your changes in the mainline "Submitting patches" guide)
    • Sometimes it makes sense to ignore notes and warnings from checkpatch if the code is more readable without fixing them.
  • Have a properly formatted one-line summary in the commit message (there is no need to write a detailed commit message, yet)
    • Look at similar commits in the commit history for examples.

The first two will be automatically checked for you when you open a PR - the CI build will fail otherwise - so you don't have to check it locally. Eventually, you should expect a few additional review comments to improve your code. This will help you to speed up submission of the patches upstream later! ;)

When this is done, then you can continue working on whatever you would like to work on! Below are a few tips and tricks for common features. However, it is hard to write a definitive guide as they differ between devices, so you will need to do some investigation on your own.


The general approach for mainlining is:

  • Look at all resources you can find, e.g.:
    • Commits in msm8916-mainline/linux
    • Existing device trees in mainline and/or commits with relevant changes (sometimes there is helpful information in the commit message)
    • The kernel documentation, especially the device tree binding documentation (all available device tree properties should be documented there)

and compare it with what you can find on downstream (e.g. device tree, kernel code, ...). Do not take the downstream device tree as a base or even copy it as-is. That is the wrong approach. Downstream code tends to be full of mistakes and unnecessarily verbose. It's easier to start from scratch and rather take something in mainline as a base.

Additional tips and tricks:

  • When making changes to kernel code compiled as modules, you need to update the kernel modules on the rootfs (they are not part of the boot image that is loaded via Fastboot). There are different options to do this:


WiFi/Bluetooth are usually provided by the Qualcomm Wireless Connectivity Subsystem (WCNSS), which is partially built into the SoC but paired with an external RF module ("iris").[1] Therefore, enabling it is usually as simple as enabling the wcnss node in the device tree:[2]

wcnss@a21b000 {
	status = "okay";

By default mainline expects WCN3620 as external RF module. However, MSM8916 can be also paired with other RF modules - another common chip is WCN3660B which has dual-band (5 GHz) support additionally. The chip identifier can be obtained from a downstream kernel log (dmesg): There should be a message like wcnss: IRIS Reg: 04000004.[3] Take the first 4 hex chars (i.e. >> 16) and compare it with the chip IDs listed in downstream.

If you do not have WCN3620, you need to override the compatible property of the iris node:[4]

wcnss@a21b000 {
	status = "okay";

	iris {
		compatible = "qcom,wcn3680"; // For WCN3660B or WCN3680

Note the use of qcom,wcn3680 instead of qcom,wcn3660 for WCN3660B. WCN3660B is actually more similar to WCN3680 since it requires different regulator voltages compared to WCN3660. qcom,wcn3680 enables the correct configuration.[4]

  • WiFi and Bluetooth share the same antenna, and they tend to conflict with each other when they are used at the same time ("WiFi/BT coexistence"). In case of problems, try disabling one of them.
  • Dual-band support on WCN3660B seems to be broken at the moment: only 2.4 GHz networks show up in the WiFi network list.


Unfortunately, WCNSS requires firmware to run. This firmware is signed, so it must be packaged separately for each device. The following files are needed:

  • wcnss.mdt plus all related segments (wcnss.b00, ...), i.e. wcnss.* (usually installed on a separate firmware (NON-HLOS) partition)
  • WCNSS_qcom_wlan_nv.bin (usually installed on the system partition, e.g. /system/etc/firmware/wlan/prima/WCNSS_qcom_wlan_nv.bin

On postmarketOS it should be packaged in a separate firmware package. Look at firmware/firmware-wileyfox-crackling as an example.

Then add a new nonfree-firmware subpackage to your device package:


nonfree_firmware() {
	pkgdesc="GPU/WiFi/BT/Video firmware"
	depends="linux-firmware-qcom firmware-vendor-device-venus firmware-vendor-device-wcnss"
	mkdir "$subpkgdir"

While you are at it, this is a good chance to package the venus.* firmware as well. It is used for hardware accelerated video codecs and should work out of the box without further changes.


Note: Due to weird dependencies issues in the DRM MSM driver, you need to make WiFi/Bluetooth work to have the DRM MSM driver finish loading successfully. (WCNSS takes a while to load, so it will trigger attempting to probe DRM MSM again after it fails to load on its own due to dependencies issues between the DRM driver and the panel driver...)

The display tends to be one of the most difficult things to get working. This is because manufacturers have many different options how to implement the display in the hardware. Generally, all devices will have a display panel connected to the SoC via MIPI Display Serial Interface (DSI). The panel can vary even for one device model; it is common to support multiple panels to be more flexible when assembling the device. As if this wasn't enough, panels are usually paired with additional controllers (e.g. for the backlight) or regulators (to provide special power supplies). Those may, or may not need to be controlled from the kernel.

The easy part is usually the panel itself. To make it work we usually only need to send some special DSI commands to initialize it. DSI Panel Driver Porting on the Freedreno wiki has an introduction how to port panels. For MSM8916 mainlining, we have created linux-mdss-dsi-panel-driver-generator which attempts to automatically parse the downstream device tree and generates panel drivers for all the supported panels.

How can you figure out which panel is being used by the device you have? The panel is usually listed in a mdss_mdp.panel= kernel parameter provided by the (primary) bootloader. You can check it from the lk2nd log, or using fastboot oem cmdline && fastboot get_staged /dev/stdout. The parameter lists the name of the panel node in the device tree.

Note: The generated panel driver should usually work without larger changes (follow the README). As usual, it is recommended to ask in the chat for further advice before running it.

You also need to make changes to the device tree. arm64: dts: msm8916-longcheer-l8150: Add display panel (here) is a good example how to add the panel to the device tree. In some case, you may need to turn on a GPIO to enable the power supply of the panel. On mainline, this is implemented using a fixed regulator. You can then use the --regulator option in the panel driver generator to add code to automatically turn on that GPIO.

Note: qcom,dsi-phy-regulator-ldo-mode is needed if, and only if you have qcom,regulator-ldo-mode in downstream. Otherwise, remove it.

Some devices have an extra backlight IC that needs to be controlled separately in the kernel. The easy case is where it is simply enabled using a GPIO: in this case it can be simply treated like a fixed regulator. But some backlight ICs have an I²C interface and then need an extra driver in the kernel.


There are different approaches used to dim the brightness of the screen. The approach used in downstream is usually defined by the qcom,mdss-dsi-bl-pmic-control-type property in the panel node of the downstream device tree:

  • Pulse-width modulation (PWM) (bl_ctrl_pwm): In this case there is an external PWM generator that generates a PWM signal for the display. PM8916 (the PMIC of MSM8916) contains one PWM generator that can be configured for the display. arm64: dts: msm8916-longcheer-l8150: Add PWM backlight support is an example how to set it up.
  • MIPI DCS commands (bl_ctrl_dcs): In this case the panel controller handles the brightness automatically. The generated panel driver should implement brightness without further changes.
  • Custom: Some devices (e.g. Samsung AMOLED panels) have bl_ctrl_dcs or something else listed, but actually use an entirely custom way to control display brightness. This needs to be implemented manually in the panel driver (or using an extra backlight driver).


The touchscreen is usually connected to a I²C bus, e.g. blsp_i2c5. Often it is either supplied by some PM8916 regulators or a set of fixed regulator. (This is often visible when the touchscreen has some "enable" GPIOs listed downstream; those usually represent a chip that can be enabled through a GPIO and then supplies a fixed voltage to the touchscreen...) This is where similarities end. There can be all sorts of touchscreens connected to the I²C bus and manufacturers like to use many different ones. Look through the supported touchscreen in mainline to see if a driver exists already, or can be modified eventually to support another version of a similar chip. Otherwise, you may need to write a simple driver yourself.




The Power and Volume Down buttons are usually connected to the PMIC (PM8916). (The Power button is - obviously - used to power on the device. Volume Down is used in combination with the Power button to force a reboot, essentially simulating removing the battery and putting it back in. On devices without removable battery holding these two buttons allows rebooting the device...)

qcom,pm8941-pwrkey (Power) and qcom,pm8941-resin (Volume Down) are used to expose these two buttons to user-space. qcom,pm8941-pwrkey is already set up in the common pm8916.dtsi and should out of the box. A new device node for qcom,pm8941-resin needs to be added manually:

&spmi_bus {
	pm8916@0 {
		pon@800 {
			volume-down {
				compatible = "qcom,pm8941-resin";
				interrupts = <0x0 0x8 1 IRQ_TYPE_EDGE_BOTH>;
				linux,code = <KEY_VOLUMEDOWN>;

Volume Up and other additional keys (e.g. Home on Samsung devices) are usually connected to a GPIO pin on the SoC. Therefore, setting them up is trivial using gpio-keys.


Like the touchscreen, sensors are usually connected to a I²C bus, e.g. blsp_i2c2 and usually supplied by L17. This is where similarities end. There can be all sorts of sensors connected to the I²C bus and manufacturers like to use many different ones. Look through the supported sensors in mainline to see if a driver exists already, or can be modified eventually to support another version of a similar chip. Otherwise, you may need to write a simple driver yourself.

Some devices have a Hall effect sensor that is used to detect if the screen is covered by some kind of flip cover. It is usually a simple GPIO like the buttons, and can be set up easily using gpio-keys.


Although PM8916 provides a linear charger for batteries only few devices actually make use of it. This might be because it is comparatively inefficient compared to a switching mode battery charger. Therefore most devices actually have custom chips that manage the battery and charging. Usually it is separated in:

  • Fuel gauge: Measures the remaining charge of the battery.
  • Charger: Handles charging the battery.

Similar to the sensors, battery/charger are usually connected to a I²C bus. Look through the supported drivers in mainline (drivers/power/supply) to see if a driver exists already, or can be modified eventually to support another version of a similar chip. Compare with the downstream driver just to be sure.

Warning: Incorrect usage of battery/charger drivers might cause the battery to malfunction. In worst case, incorrect charging of batteries might even cause the battery to explode. Be careful.


The PMIC used together with MSM8916.
"BAM-enabled low-speed peripheral". Controllers for UART, I²C and SPI.
Low-power audio subsystem
Mobile Display Subsystem - the hardware that manages the display.
Part of MDSS that manages display panels.
MIPI Display Serial Interface (DSI) - used for communication with display panel.
Camera Subsystem
System Power Management Interface - used for communication between MSM8916 and PM8916.

Remote Processors

Wireless Connectivity Subsystem (WCNSS). Provides WiFi and Bluetooth.
Provides hardware-accelerated video encoding/decoding.
Provides mobile connectivity and Audio DSP (ADSP). Note: On MSM8916, modem and ADSP are one remote processor, but the ADSP is usually separate on Qualcomm SoCs.
Microarchitecture for DSPs, used for ADSP (and therefore also modem in MSM8916).


  • [PATCH v2 0/9] Qualcomm WCNSS remoteproc (patch on Linux mainline mailing list)
  • arm64: dts: msm8916-samsung-a2015: Enable WCNSS for WiFi and BT (mainline kernel patch)
  • drivers/net/wireless/wcnss/wcnss_vreg.c, line 366 (downstream kernel code)
  • 4.0 4.1 arm64: dts: msm8916-samsung-a5u: Override iris compatible (mainline kernel patch)