From 4db6f31f72c35c7067f737bbc949439ddaf36888 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Wed, 7 Jan 2026 16:31:18 +0000 Subject: [PATCH 1/5] sm8250: bump edge to 6.18 Copies and modifications to some patches from SM8250-6.12 Signed-off-by: CodeChenL <2540735020@qq.com> --- config/sources/families/sm8250.conf | 4 +- .../sm8250-6.18/0000.patching_config.yaml | 29 + .../0001-drm-Add-drm-notifier-support.patch | 135 + ...Input-Add-nt36523-touchscreen-driver.patch | 3495 +++++++++++++++++ ...m8250-xiaomi-elish-enable-touchscree.patch | 91 + ...006-ASoC-qcom-sm8250-Add-tdm-support.patch | 126 + ...m8250-xiaomi-elish-Add-sound-support.patch | 281 ++ ...0008-Asoc-wm_adsp-Add-prefix-support.patch | 44 + ...m8250-oneplus-instantnoodlep-Add-dev.patch | 851 ++++ ...m8250-xiaomi-elish-add-keyboard-supp.patch | 77 + ...m8250-xiaomi-elish-remove-framebuffe.patch | 51 + ...019-input-nt36xxx-Enable-pen-support.patch | 118 + ...020-nt36xxx-add-pen-input-resolution.patch | 58 + ...m8250-oneplus-instantnoodlep-Restore.patch | 217 + ..._adsp-Use-xiaomi-elish-firmware-name.patch | 25 + ...ort-the-firmware-download-delay-from.patch | 25 + ...creen-add-Synaptics-TCM-oncell-S3908.patch | 640 +++ ...-arm64-dts-qcom-add-OnePlus-8T-kebab.patch | 975 +++++ ...8-Input-driver-for-AYN-Odin2-Gamepad.patch | 362 ++ ...put-add-Qualcomm-SPMI-haptics-driver.patch | 1090 +++++ ...-qcom-pm8150b-introduce-spmi-haptics.patch | 47 + .../archive/sm8250-6.18/dt/.placeholder | 0 22 files changed, 8739 insertions(+), 2 deletions(-) create mode 100644 patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml create mode 100644 patch/kernel/archive/sm8250-6.18/0001-drm-Add-drm-notifier-support.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0003-Input-Add-nt36523-touchscreen-driver.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0005-arm64-dts-qcom-sm8250-xiaomi-elish-enable-touchscree.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0006-ASoC-qcom-sm8250-Add-tdm-support.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0007-arm64-dts-qcom-sm8250-xiaomi-elish-Add-sound-support.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0008-Asoc-wm_adsp-Add-prefix-support.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0009-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Add-dev.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0016-arm64-dts-qcom-sm8250-xiaomi-elish-add-keyboard-supp.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0017-arm64-dts-qcom-sm8250-xiaomi-elish-remove-framebuffe.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0019-input-nt36xxx-Enable-pen-support.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0020-nt36xxx-add-pen-input-resolution.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0021-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Restore.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0023-Asoc-wm_adsp-Use-xiaomi-elish-firmware-name.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0024-input-nt36523-short-the-firmware-download-delay-from.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0027-Input-touchscreen-add-Synaptics-TCM-oncell-S3908.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0028-arm64-dts-qcom-add-OnePlus-8T-kebab.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0038-Input-driver-for-AYN-Odin2-Gamepad.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0039-input-add-Qualcomm-SPMI-haptics-driver.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0040-arm64-dts-qcom-pm8150b-introduce-spmi-haptics.patch create mode 100644 patch/kernel/archive/sm8250-6.18/dt/.placeholder diff --git a/config/sources/families/sm8250.conf b/config/sources/families/sm8250.conf index 814bf8794383..69772d2a6e8f 100644 --- a/config/sources/families/sm8250.conf +++ b/config/sources/families/sm8250.conf @@ -22,8 +22,8 @@ case $BRANCH in ;; edge) - declare -g KERNEL_MAJOR_MINOR="6.14" # Major and minor versions of this kernel. - declare -g KERNELBRANCH='branch:linux-6.14.y' + declare -g KERNEL_MAJOR_MINOR="6.18" # Major and minor versions of this kernel. + declare -g KERNELBRANCH='branch:linux-6.18.y' declare -g -i KERNEL_GIT_CACHE_TTL=120 # 2 minutes; this is a high-traffic repo ;; diff --git a/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml b/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml new file mode 100644 index 000000000000..38f229666adf --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml @@ -0,0 +1,29 @@ +config: # This is file 'patch/kernel/archive/sm8250-6.7/0000.patching_config.yaml' + + # Just some info stuff; not used by the patching scripts + name: sm8250-6.12 + kind: kernel + type: mainline # or: vendor + branch: linux-6.12.y + last-known-good-tag: v6.12.5 + maintainers: + - { github: rpardini, name: Ricardo Pardini, email: ricardo@pardini.net, armbian-forum: rpardini } + + # .dts files in these directories will be copied as-is to the build tree; later ones overwrite earlier ones. + # This is meant to provide a way to "add a board DTS" without having to null-patch them in. + dts-directories: + - { source: "dt", target: "arch/arm64/boot/dts/qcom" } + + # the Makefile in each of these directories will be magically patched to include the dts files copied + # or patched-in; overlay subdir will be included "-y" if it exists. + # No more Makefile patching needed, yay! + auto-patch-dt-makefile: + - { directory: "arch/arm64/boot/dts/qcom", config-var: "CONFIG_ARCH_QCOM" } + + # configuration for when applying patches to git / auto-rewriting patches (development cycle helpers) + patches-to-git: + do-not-commit-files: + - "MAINTAINERS" # constant churn, drop them. sorry. + do-not-commit-regexes: # Python-style regexes + - "^arch/([a-zA-Z0-9]+)/boot/dts/([a-zA-Z0-9]+)/Makefile$" # ignore DT Makefile patches, we've an auto-patcher now + diff --git a/patch/kernel/archive/sm8250-6.18/0001-drm-Add-drm-notifier-support.patch b/patch/kernel/archive/sm8250-6.18/0001-drm-Add-drm-notifier-support.patch new file mode 100644 index 000000000000..9f9c44c2c96e --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0001-drm-Add-drm-notifier-support.patch @@ -0,0 +1,135 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Thu, 4 Aug 2022 13:26:53 +0800 +Subject: drm: Add drm notifier support + +--- + drivers/gpu/drm/Makefile | 3 +- + drivers/gpu/drm/drm_notifier.c | 58 ++++++++++ + include/drm/drm_notifier.h | 37 ++++++ + 3 files changed, 97 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/gpu/drm/Makefile ++++ b/drivers/gpu/drm/Makefile +@@ -74,7 +74,8 @@ drm-y := \ + drm_vblank.o \ + drm_vblank_work.o \ + drm_vma_manager.o \ +- drm_writeback.o ++ drm_writeback.o \ ++ drm_notifier.o + drm-$(CONFIG_DRM_CLIENT) += \ + drm_client.o \ + drm_client_event.o \ +diff --git a/drivers/gpu/drm/drm_notifier.c b/drivers/gpu/drm/drm_notifier.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/gpu/drm/drm_notifier.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2019, The Linux Foundation. All rights reserved. ++ * Copyright (C) 2021 XiaoMi, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++ ++static BLOCKING_NOTIFIER_HEAD(mi_drm_notifier_list); ++ ++/** ++ * mi_drm_register_client - register a client notifier ++ * @nb: notifier block to callback on events ++ * ++ * This function registers a notifier callback function ++ * to msm_drm_notifier_list, which would be called when ++ * received unblank/power down event. ++ */ ++int mi_drm_register_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&mi_drm_notifier_list, nb); ++} ++EXPORT_SYMBOL(mi_drm_register_client); ++ ++/** ++ * mi_drm_unregister_client - unregister a client notifier ++ * @nb: notifier block to callback on events ++ * ++ * This function unregisters the callback function from ++ * msm_drm_notifier_list. ++ */ ++int mi_drm_unregister_client(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&mi_drm_notifier_list, nb); ++} ++EXPORT_SYMBOL(mi_drm_unregister_client); ++ ++/** ++ * mi_drm_notifier_call_chain - notify clients of drm_events ++ * @val: event MSM_DRM_EARLY_EVENT_BLANK or MSM_DRM_EVENT_BLANK ++ * @v: notifier data, inculde display id and display blank ++ * event(unblank or power down). ++ */ ++int mi_drm_notifier_call_chain(unsigned long val, void *v) ++{ ++ return blocking_notifier_call_chain(&mi_drm_notifier_list, val, v); ++} ++EXPORT_SYMBOL(mi_drm_notifier_call_chain); +diff --git a/include/drm/drm_notifier.h b/include/drm/drm_notifier.h +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/include/drm/drm_notifier.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 2019, The Linux Foundation. All rights reserved. ++ * Copyright (C) 2021 XiaoMi, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _DRM_NOTIFIER_H_ ++#define _DRM_NOTIFIER_H_ ++ ++#include ++ ++/* A hardware display blank change occurred */ ++#define MI_DRM_EVENT_BLANK 0x01 ++/* A hardware display blank early change occurred */ ++#define MI_DRM_EARLY_EVENT_BLANK 0x02 ++ ++enum drm_notifier_data { ++ /* panel: power on */ ++ MI_DRM_BLANK_UNBLANK, ++ /* panel: power down */ ++ MI_DRM_BLANK_POWERDOWN, ++}; ++ ++int mi_drm_register_client(struct notifier_block *nb); ++int mi_drm_unregister_client(struct notifier_block *nb); ++int mi_drm_notifier_call_chain(unsigned long val, void *v); ++ ++#endif /* _DRM_NOTIFIER_H */ +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0003-Input-Add-nt36523-touchscreen-driver.patch b/patch/kernel/archive/sm8250-6.18/0003-Input-Add-nt36523-touchscreen-driver.patch new file mode 100644 index 000000000000..41aec6a8f27e --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0003-Input-Add-nt36523-touchscreen-driver.patch @@ -0,0 +1,3495 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Thu, 4 Aug 2022 13:26:26 +0800 +Subject: Input: Add nt36523 touchscreen driver + +--- + drivers/input/touchscreen/Kconfig | 2 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/nt36523/Kconfig | 11 + + drivers/input/touchscreen/nt36523/Makefile | 8 + + drivers/input/touchscreen/nt36523/nt36xxx.c | 1910 ++++++++++ + drivers/input/touchscreen/nt36523/nt36xxx.h | 242 ++ + drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c | 857 +++++ + drivers/input/touchscreen/nt36523/nt36xxx_mem_map.h | 390 ++ + 8 files changed, 3421 insertions(+) + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -12,6 +12,8 @@ menuconfig INPUT_TOUCHSCREEN + + if INPUT_TOUCHSCREEN + ++source "drivers/input/touchscreen/nt36523/Kconfig" ++ + config TOUCHSCREEN_88PM860X + tristate "Marvell 88PM860x touchscreen" + depends on MFD_88PM860X +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -117,3 +117,4 @@ obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o + obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o + obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o + obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o ++obj-$(CONFIG_TOUCHSCREEN_NT36523_SPI) += nt36523/ +diff --git a/drivers/input/touchscreen/nt36523/Kconfig b/drivers/input/touchscreen/nt36523/Kconfig +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/nt36523/Kconfig +@@ -0,0 +1,11 @@ ++# ++# Novatek NT36523 touchscreen driver configuration ++# ++config TOUCHSCREEN_NT36523_SPI ++ tristate "Novatek NT36523 no flash SPI driver" ++ default n ++ help ++ Say Y here if you have a Novatek NT36523 no flash touchscreen connected ++ to your system by SPI bus. ++ ++ If unsure, say N. +diff --git a/drivers/input/touchscreen/nt36523/Makefile b/drivers/input/touchscreen/nt36523/Makefile +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/nt36523/Makefile +@@ -0,0 +1,8 @@ ++# ++# Makefile for the Novatek NT36523 touchscreen driver. ++# ++ ++# Each configuration option enables a list of files. ++obj-$(CONFIG_TOUCHSCREEN_NT36523_SPI) += nt36523_ts.o ++nt36523_ts-y := nt36xxx.o \ ++ nt36xxx_fw_update.o +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.c b/drivers/input/touchscreen/nt36523/nt36xxx.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.c +@@ -0,0 +1,1910 @@ ++/* ++ * Copyright (C) 2010 - 2018 Novatek, Inc. ++ * Copyright (C) 2021 XiaoMi, Inc. ++ * ++ * $Revision: 73033 $ ++ * $Date: 2020-11-26 10:09:14 +0800 (週四, 26 十一月 2020) $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_DRM ++#include ++#endif ++ ++#include "nt36xxx.h" ++ ++#if NVT_TOUCH_ESD_PROTECT ++#include ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++#if NVT_TOUCH_ESD_PROTECT ++static struct delayed_work nvt_esd_check_work; ++static struct workqueue_struct *nvt_esd_check_wq; ++static unsigned long irq_timer = 0; ++uint8_t esd_check = false; ++uint8_t esd_retry = 0; ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++struct nvt_ts_data *ts; ++ ++#if BOOT_UPDATE_FIRMWARE ++static struct workqueue_struct *nvt_fwu_wq; ++extern void Boot_Update_Firmware(struct work_struct *work); ++#endif ++ ++#ifdef CONFIG_DRM ++static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); ++#endif ++ ++static int32_t nvt_ts_suspend(struct device *dev); ++static int32_t nvt_ts_resume(struct device *dev); ++ ++uint32_t ENG_RST_ADDR = 0x7FFF80; ++uint32_t SWRST_N8_ADDR = 0; //read from dtsi ++uint32_t SPI_RD_FAST_ADDR = 0; //read from dtsi ++ ++#if TOUCH_KEY_NUM > 0 ++const uint16_t touch_key_array[TOUCH_KEY_NUM] = { ++ KEY_BACK, ++ KEY_HOME, ++ KEY_MENU ++}; ++#endif ++ ++static uint8_t bTouchIsAwake = 0; ++ ++/******************************************************* ++Description: ++ Novatek touchscreen irq enable/disable function. ++ ++return: ++ n.a. ++*******************************************************/ ++static void nvt_irq_enable(bool enable) ++{ ++ if (enable) { ++ if (!ts->irq_enabled) { ++ enable_irq(ts->client->irq); ++ ts->irq_enabled = true; ++ } ++ } else { ++ if (ts->irq_enabled) { ++ disable_irq(ts->client->irq); ++ ts->irq_enabled = false; ++ } ++ } ++} ++ ++static inline int32_t spi_read_write(struct spi_device *client, uint8_t *buf, size_t len , NVT_SPI_RW rw) ++{ ++ struct spi_message m; ++ struct spi_transfer t = { ++ .len = len, ++ }; ++ ++ memset(ts->xbuf, 0, len + DUMMY_BYTES); ++ memcpy(ts->xbuf, buf, len); ++ ++ switch (rw) { ++ case NVTREAD: ++ t.tx_buf = ts->xbuf; ++ t.rx_buf = ts->rbuf; ++ t.len = (len + DUMMY_BYTES); ++ break; ++ ++ case NVTWRITE: ++ t.tx_buf = ts->xbuf; ++ break; ++ } ++ ++ spi_message_init(&m); ++ spi_message_add_tail(&t, &m); ++ return spi_sync(client, &m); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen spi read function. ++ ++return: ++ Executive outcomes. 2---succeed. -5---I/O error ++*******************************************************/ ++int32_t CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len) ++{ ++ int32_t ret = -1; ++ int32_t retries = 0; ++ ++ mutex_lock(&ts->xbuf_lock); ++ ++ buf[0] = SPI_READ_MASK(buf[0]); ++ ++ while (retries < 5) { ++ ret = spi_read_write(client, buf, len, NVTREAD); ++ if (ret == 0) break; ++ retries++; ++ } ++ ++ if (unlikely(retries == 5)) { ++ NVT_ERR("read error, ret=%d\n", ret); ++ ret = -EIO; ++ } else { ++ memcpy((buf+1), (ts->rbuf+2), (len-1)); ++ } ++ ++ mutex_unlock(&ts->xbuf_lock); ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen spi write function. ++ ++return: ++ Executive outcomes. 1---succeed. -5---I/O error ++*******************************************************/ ++int32_t CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len) ++{ ++ int32_t ret = -1; ++ int32_t retries = 0; ++ ++ mutex_lock(&ts->xbuf_lock); ++ ++ buf[0] = SPI_WRITE_MASK(buf[0]); ++ ++ while (retries < 5) { ++ ret = spi_read_write(client, buf, len, NVTWRITE); ++ if (ret == 0) break; ++ retries++; ++ } ++ ++ if (unlikely(retries == 5)) { ++ NVT_ERR("error, ret=%d\n", ret); ++ ret = -EIO; ++ } ++ ++ mutex_unlock(&ts->xbuf_lock); ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen set index/page/addr address. ++ ++return: ++ Executive outcomes. 0---succeed. -5---access fail. ++*******************************************************/ ++int32_t nvt_set_page(uint32_t addr) ++{ ++ uint8_t buf[4] = {0}; ++ ++ buf[0] = 0xFF; //set index/page/addr command ++ buf[1] = (addr >> 15) & 0xFF; ++ buf[2] = (addr >> 7) & 0xFF; ++ ++ return CTP_SPI_WRITE(ts->client, buf, 3); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen write data to specify address. ++ ++return: ++ Executive outcomes. 0---succeed. -5---access fail. ++*******************************************************/ ++int32_t nvt_write_addr(uint32_t addr, uint8_t data) ++{ ++ int32_t ret = 0; ++ uint8_t buf[4] = {0}; ++ ++ //---set xdata index--- ++ buf[0] = 0xFF; //set index/page/addr command ++ buf[1] = (addr >> 15) & 0xFF; ++ buf[2] = (addr >> 7) & 0xFF; ++ ret = CTP_SPI_WRITE(ts->client, buf, 3); ++ if (ret) { ++ NVT_ERR("set page 0x%06X failed, ret = %d\n", addr, ret); ++ return ret; ++ } ++ ++ //---write data to index--- ++ buf[0] = addr & (0x7F); ++ buf[1] = data; ++ ret = CTP_SPI_WRITE(ts->client, buf, 2); ++ if (ret) { ++ NVT_ERR("write data to 0x%06X failed, ret = %d\n", addr, ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen enable hw bld crc function. ++ ++return: ++ N/A. ++*******************************************************/ ++void nvt_bld_crc_enable(void) ++{ ++ uint8_t buf[4] = {0}; ++ ++ //---set xdata index to BLD_CRC_EN_ADDR--- ++ nvt_set_page(ts->mmap->BLD_CRC_EN_ADDR); ++ ++ //---read data from index--- ++ buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); ++ buf[1] = 0xFF; ++ CTP_SPI_READ(ts->client, buf, 2); ++ ++ //---write data to index--- ++ buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); ++ buf[1] = buf[1] | (0x01 << 7); ++ CTP_SPI_WRITE(ts->client, buf, 2); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen clear status & enable fw crc function. ++ ++return: ++ N/A. ++*******************************************************/ ++void nvt_fw_crc_enable(void) ++{ ++ uint8_t buf[4] = {0}; ++ ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR); ++ ++ //---clear fw reset status--- ++ buf[0] = EVENT_MAP_RESET_COMPLETE & (0x7F); ++ buf[1] = 0x00; ++ CTP_SPI_WRITE(ts->client, buf, 2); ++ ++ //---enable fw crc--- ++ buf[0] = EVENT_MAP_HOST_CMD & (0x7F); ++ buf[1] = 0xAE; //enable fw crc command ++ CTP_SPI_WRITE(ts->client, buf, 2); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen set boot ready function. ++ ++return: ++ N/A. ++*******************************************************/ ++void nvt_boot_ready(void) ++{ ++ //---write BOOT_RDY status cmds--- ++ nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 1); ++ ++ mdelay(5); ++ ++ if (!ts->hw_crc) { ++ //---write BOOT_RDY status cmds--- ++ nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 0); ++ ++ //---write POR_CD cmds--- ++ nvt_write_addr(ts->mmap->POR_CD_ADDR, 0xA0); ++ } ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen enable auto copy mode function. ++ ++return: ++ N/A. ++*******************************************************/ ++void nvt_tx_auto_copy_mode(void) ++{ ++ //---write TX_AUTO_COPY_EN cmds--- ++ nvt_write_addr(ts->mmap->TX_AUTO_COPY_EN, 0x69); ++ ++ NVT_ERR("tx auto copy mode enable\n"); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen check spi dma tx info function. ++ ++return: ++ N/A. ++*******************************************************/ ++int32_t nvt_check_spi_dma_tx_info(void) ++{ ++ uint8_t buf[8] = {0}; ++ int32_t i = 0; ++ const int32_t retry = 200; ++ ++ for (i = 0; i < retry; i++) { ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->SPI_DMA_TX_INFO); ++ ++ //---read fw status--- ++ buf[0] = ts->mmap->SPI_DMA_TX_INFO & 0x7F; ++ buf[1] = 0xFF; ++ CTP_SPI_READ(ts->client, buf, 2); ++ ++ if (buf[1] == 0x00) ++ break; ++ ++ usleep_range(1000, 1000); ++ } ++ ++ if (i >= retry) { ++ NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen eng reset cmd ++ function. ++ ++return: ++ n.a. ++*******************************************************/ ++void nvt_eng_reset(void) ++{ ++ //---eng reset cmds to ENG_RST_ADDR--- ++ nvt_write_addr(ENG_RST_ADDR, 0x5A); ++ ++ mdelay(1); //wait tMCU_Idle2TP_REX_Hi after TP_RST ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen reset MCU ++ function. ++ ++return: ++ n.a. ++*******************************************************/ ++void nvt_sw_reset(void) ++{ ++ //---software reset cmds to SWRST_N8_ADDR--- ++ nvt_write_addr(SWRST_N8_ADDR, 0x55); ++ ++ msleep(10); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen reset MCU then into idle mode ++ function. ++ ++return: ++ n.a. ++*******************************************************/ ++void nvt_sw_reset_idle(void) ++{ ++ //---MCU idle cmds to SWRST_N8_ADDR--- ++ nvt_write_addr(SWRST_N8_ADDR, 0xAA); ++ ++ msleep(15); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen reset MCU (boot) function. ++ ++return: ++ n.a. ++*******************************************************/ ++void nvt_bootloader_reset(void) ++{ ++ //---reset cmds to SWRST_N8_ADDR--- ++ nvt_write_addr(SWRST_N8_ADDR, 0x69); ++ ++ mdelay(5); //wait tBRST2FR after Bootload RST ++ ++ if (SPI_RD_FAST_ADDR) { ++ /* disable SPI_RD_FAST */ ++ nvt_write_addr(SPI_RD_FAST_ADDR, 0x00); ++ } ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen clear FW status function. ++ ++return: ++ Executive outcomes. 0---succeed. -1---fail. ++*******************************************************/ ++int32_t nvt_clear_fw_status(void) ++{ ++ uint8_t buf[8] = {0}; ++ int32_t i = 0; ++ const int32_t retry = 20; ++ ++ for (i = 0; i < retry; i++) { ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); ++ ++ //---clear fw status--- ++ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; ++ buf[1] = 0x00; ++ CTP_SPI_WRITE(ts->client, buf, 2); ++ ++ //---read fw status--- ++ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; ++ buf[1] = 0xFF; ++ CTP_SPI_READ(ts->client, buf, 2); ++ ++ if (buf[1] == 0x00) ++ break; ++ ++ usleep_range(10000, 10000); ++ } ++ ++ if (i >= retry) { ++ NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen check FW status function. ++ ++return: ++ Executive outcomes. 0---succeed. -1---failed. ++*******************************************************/ ++int32_t nvt_check_fw_status(void) ++{ ++ uint8_t buf[8] = {0}; ++ int32_t i = 0; ++ const int32_t retry = 50; ++ ++ for (i = 0; i < retry; i++) { ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); ++ ++ //---read fw status--- ++ buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; ++ buf[1] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 2); ++ ++ if ((buf[1] & 0xF0) == 0xA0) ++ break; ++ ++ usleep_range(10000, 10000); ++ } ++ ++ if (i >= retry) { ++ NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen check FW reset state function. ++ ++return: ++ Executive outcomes. 0---succeed. -1---failed. ++*******************************************************/ ++int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state) ++{ ++ uint8_t buf[8] = {0}; ++ int32_t ret = 0; ++ int32_t retry = 0; ++ int32_t retry_max = (check_reset_state == RESET_STATE_INIT) ? 10 : 50; ++ ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE); ++ ++ while (1) { ++ //---read reset state--- ++ buf[0] = EVENT_MAP_RESET_COMPLETE; ++ buf[1] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 6); ++ ++ if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX)) { ++ ret = 0; ++ break; ++ } ++ ++ retry++; ++ if(unlikely(retry > retry_max)) { ++ NVT_ERR("error, retry=%d, buf[1]=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", ++ retry, buf[1], buf[2], buf[3], buf[4], buf[5]); ++ ret = -1; ++ break; ++ } ++ ++ usleep_range(10000, 10000); ++ } ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen get novatek project id information ++ function. ++ ++return: ++ Executive outcomes. 0---success. -1---fail. ++*******************************************************/ ++int32_t nvt_read_pid(void) ++{ ++ uint8_t buf[4] = {0}; ++ int32_t ret = 0; ++ ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID); ++ ++ //---read project id--- ++ buf[0] = EVENT_MAP_PROJECTID; ++ buf[1] = 0x00; ++ buf[2] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 3); ++ ++ ts->nvt_pid = (buf[2] << 8) + buf[1]; ++ ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR); ++ ++ NVT_LOG("PID=%04X\n", ts->nvt_pid); ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen get firmware related information ++ function. ++ ++return: ++ Executive outcomes. 0---success. -1---fail. ++*******************************************************/ ++int32_t nvt_get_fw_info(void) ++{ ++ uint8_t buf[64] = {0}; ++ uint32_t retry_count = 0; ++ int32_t ret = 0; ++ ++info_retry: ++ //---set xdata index to EVENT BUF ADDR--- ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); ++ ++ //---read fw info--- ++ buf[0] = EVENT_MAP_FWINFO; ++ CTP_SPI_READ(ts->client, buf, 39); ++ ts->fw_ver = buf[1]; ++ ts->x_num = buf[3]; ++ ts->y_num = buf[4]; ++ ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]); ++ ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]); ++ ts->max_button_num = buf[11]; ++ ts->cascade = buf[34] & 0x01; ++ if (ts->pen_support) { ++ ts->x_gang_num = buf[37]; ++ ts->y_gang_num = buf[38]; ++ } ++ ++ //---clear x_num, y_num if fw info is broken--- ++ if ((buf[1] + buf[2]) != 0xFF) { ++ NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]); ++ ts->fw_ver = 0; ++ ts->x_num = 18; ++ ts->y_num = 32; ++ ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; ++ ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; ++ ts->max_button_num = TOUCH_KEY_NUM; ++ ++ if(retry_count < 3) { ++ retry_count++; ++ NVT_ERR("retry_count=%d\n", retry_count); ++ goto info_retry; ++ } else { ++ NVT_ERR("Set default fw_ver=%d, x_num=%d, y_num=%d, " ++ "abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n", ++ ts->fw_ver, ts->x_num, ts->y_num, ++ ts->abs_x_max, ts->abs_y_max, ts->max_button_num); ++ ret = -1; ++ } ++ } else { ++ ret = 0; ++ } ++ ++ NVT_LOG("fw_ver = 0x%02X, fw_type = 0x%02X, x_num=%d, y_num=%d\n", ts->fw_ver, buf[14], ts->x_num, ts->y_num); ++ ++ //---Get Novatek PID--- ++ nvt_read_pid(); ++ ++ return ret; ++} ++ ++static void release_pen_event(void) { ++ if (ts && ts->pen_input_dev) { ++ input_report_abs(ts->pen_input_dev, ABS_X, 0); ++ input_report_abs(ts->pen_input_dev, ABS_Y, 0); ++ input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0); ++ input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0); ++ input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0); ++ input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0); ++ input_report_key(ts->pen_input_dev, BTN_TOUCH, 0); ++ input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); ++ input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); ++ input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); ++ input_sync(ts->pen_input_dev); ++ } ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen parse device tree function. ++ ++return: ++ n.a. ++*******************************************************/ ++#ifdef CONFIG_OF ++static int32_t nvt_parse_dt(struct device *dev) ++{ ++ struct device_node *np = dev->of_node; ++ int32_t ret = 0; ++ ++#if NVT_TOUCH_SUPPORT_HW_RST ++ ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags); ++ NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio); ++#endif ++ ts->irq_gpio = of_get_named_gpio(np, "novatek,irq-gpio", 0); ++ NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio); ++ ++ ts->pen_support = of_property_read_bool(np, "novatek,pen-support"); ++ NVT_LOG("novatek,pen-support=%d\n", ts->pen_support); ++ ++ ts->wgp_stylus = of_property_read_bool(np, "novatek,wgp-stylus"); ++ NVT_LOG("novatek,wgp-stylus=%d\n", ts->wgp_stylus); ++ ++ ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &SWRST_N8_ADDR); ++ if (ret) { ++ NVT_ERR("error reading novatek,swrst-n8-addr. ret=%d\n", ret); ++ return ret; ++ } else { ++ NVT_LOG("SWRST_N8_ADDR=0x%06X\n", SWRST_N8_ADDR); ++ } ++ ++ ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &SPI_RD_FAST_ADDR); ++ if (ret) { ++ NVT_LOG("not support novatek,spi-rd-fast-addr\n"); ++ SPI_RD_FAST_ADDR = 0; ++ ret = 0; ++ } else { ++ NVT_LOG("SPI_RD_FAST_ADDR=0x%06X\n", SPI_RD_FAST_ADDR); ++ } ++ ++ ret = of_property_read_string(np, "firmware-name", &ts->fw_name); ++ if (ret) { ++ NVT_LOG("Unable to get touchscreen firmware name\n"); ++ ts->fw_name = DEFAULT_BOOT_UPDATE_FIRMWARE_NAME; ++ } ++ ++ ret = of_property_read_u32(np, "spi-max-frequency", &ts->spi_max_freq); ++ if (ret) { ++ NVT_LOG("Unable to get spi freq\n"); ++ return ret; ++ } else { ++ NVT_LOG("spi-max-frequency: %u\n", ts->spi_max_freq); ++ } ++ ++ return ret; ++} ++#else ++static int32_t nvt_parse_dt(struct device *dev) ++{ ++#if NVT_TOUCH_SUPPORT_HW_RST ++ ts->reset_gpio = NVTTOUCH_RST_PIN; ++#endif ++ ts->irq_gpio = NVTTOUCH_INT_PIN; ++ return 0; ++} ++#endif ++ ++/******************************************************* ++Description: ++ Novatek touchscreen config and request gpio ++ ++return: ++ Executive outcomes. 0---succeed. not 0---failed. ++*******************************************************/ ++static int nvt_gpio_config(struct nvt_ts_data *ts) ++{ ++ int32_t ret = 0; ++ ++#if NVT_TOUCH_SUPPORT_HW_RST ++ /* request RST-pin (Output/High) */ ++ if (gpio_is_valid(ts->reset_gpio)) { ++ ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_LOW, "NVT-tp-rst"); ++ if (ret) { ++ NVT_ERR("Failed to request NVT-tp-rst GPIO\n"); ++ goto err_request_reset_gpio; ++ } ++ } ++#endif ++ ++ /* request INT-pin (Input) */ ++ if (gpio_is_valid(ts->irq_gpio)) { ++ ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int"); ++ if (ret) { ++ NVT_ERR("Failed to request NVT-int GPIO\n"); ++ goto err_request_irq_gpio; ++ } ++ } ++ ++ return ret; ++ ++err_request_irq_gpio: ++#if NVT_TOUCH_SUPPORT_HW_RST ++ gpio_free(ts->reset_gpio); ++err_request_reset_gpio: ++#endif ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen deconfig gpio ++ ++return: ++ n.a. ++*******************************************************/ ++static void nvt_gpio_deconfig(struct nvt_ts_data *ts) ++{ ++ if (gpio_is_valid(ts->irq_gpio)) ++ gpio_free(ts->irq_gpio); ++#if NVT_TOUCH_SUPPORT_HW_RST ++ if (gpio_is_valid(ts->reset_gpio)) ++ gpio_free(ts->reset_gpio); ++#endif ++} ++ ++static uint8_t nvt_fw_recovery(uint8_t *point_data) ++{ ++ uint8_t i = 0; ++ uint8_t detected = true; ++ ++ /* check pattern */ ++ for (i=1 ; i<7 ; i++) { ++ if (point_data[i] != 0x77) { ++ detected = false; ++ break; ++ } ++ } ++ ++ return detected; ++} ++ ++void nvt_set_dbgfw_status(bool enable) ++{ ++ ts->fw_debug = enable; ++} ++ ++bool nvt_get_dbgfw_status(void) ++{ ++ return ts->fw_debug; ++} ++ ++#if NVT_TOUCH_ESD_PROTECT ++void nvt_esd_check_enable(uint8_t enable) ++{ ++ /* update interrupt timer */ ++ irq_timer = jiffies; ++ /* clear esd_retry counter, if protect function is enabled */ ++ esd_retry = enable ? 0 : esd_retry; ++ /* enable/disable esd check flag */ ++ esd_check = enable; ++} ++ ++static void nvt_esd_check_func(struct work_struct *work) ++{ ++ unsigned int timer = jiffies_to_msecs(jiffies - irq_timer); ++ ++ //NVT_LOG("esd_check = %d (retry %d)\n", esd_check, esd_retry); //DEBUG ++ ++ if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) { ++ mutex_lock(&ts->lock); ++ NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry); ++ /* do esd recovery, reload fw */ ++ nvt_update_firmware(ts->fw_name); ++ mutex_unlock(&ts->lock); ++ /* update interrupt timer */ ++ irq_timer = jiffies; ++ /* update esd_retry counter */ ++ esd_retry++; ++ } ++ ++ queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, ++ msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); ++} ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++#if NVT_TOUCH_WDT_RECOVERY ++static uint8_t recovery_cnt = 0; ++static uint8_t nvt_wdt_fw_recovery(uint8_t *point_data) ++{ ++ uint32_t recovery_cnt_max = 10; ++ uint8_t recovery_enable = false; ++ uint8_t i = 0; ++ ++ recovery_cnt++; ++ ++ /* check pattern */ ++ for (i=1 ; i<7 ; i++) { ++ if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) { ++ recovery_cnt = 0; ++ break; ++ } ++ } ++ ++ if (recovery_cnt > recovery_cnt_max){ ++ recovery_enable = true; ++ recovery_cnt = 0; ++ } ++ ++ return recovery_enable; ++} ++#endif /* #if NVT_TOUCH_WDT_RECOVERY */ ++ ++#define PEN_DATA_LEN 14 ++#define FW_HISTORY_SIZE 128 ++static uint32_t nvt_dump_fw_history(void) ++{ ++ int32_t ret = 0; ++ uint8_t buf[FW_HISTORY_SIZE + 1 + DUMMY_BYTES] = {0}; ++ int32_t i = 0; ++ char *tmp_dump = NULL; ++ int32_t line_cnt = 0; ++ ++ if (ts->mmap->FW_HISTORY_ADDR == 0) { ++ NVT_ERR("FW_HISTORY_ADDR not available!\n"); ++ ret = -1; ++ goto exit_nvt_dump_fw_history; ++ } ++ nvt_set_page(ts->mmap->FW_HISTORY_ADDR); ++ buf[0] = ts->mmap->FW_HISTORY_ADDR & 0xFF; ++ CTP_SPI_READ(ts->client, buf, FW_HISTORY_SIZE + 1); ++ if (ret) { ++ NVT_ERR("CTP_SPI_READ failed.(%d)\n", ret); ++ ret = -1; ++ goto exit_nvt_dump_fw_history; ++ } ++ ++ tmp_dump = (char *)kzalloc(FW_HISTORY_SIZE * 4, GFP_KERNEL); ++ for (i = 0; i < FW_HISTORY_SIZE; i++) { ++ sprintf(tmp_dump + i * 3 + line_cnt, "%02X ", buf[1 + i]); ++ if ((i + 1) % 16 == 0) { ++ sprintf(tmp_dump + i * 3 + line_cnt + 3, "%c", '\n'); ++ line_cnt++; ++ } ++ } ++ NVT_LOG("%s", tmp_dump); ++ ++exit_nvt_dump_fw_history: ++ if (tmp_dump) { ++ kfree(tmp_dump); ++ tmp_dump = NULL; ++ } ++ nvt_set_page(ts->mmap->EVENT_BUF_ADDR); ++ ++ return ret; ++} ++ ++#define POINT_DATA_LEN 65 ++/******************************************************* ++Description: ++ Novatek touchscreen work function. ++ ++return: ++ n.a. ++*******************************************************/ ++static irqreturn_t nvt_ts_work_func(int irq, void *data) ++{ ++ int32_t ret = -1; ++ uint8_t point_data[POINT_DATA_LEN + PEN_DATA_LEN + 1 + DUMMY_BYTES] = {0}; ++ uint32_t position = 0; ++ uint32_t input_x = 0; ++ uint32_t input_y = 0; ++ uint32_t input_w = 0; ++ uint32_t input_p = 0; ++ uint8_t input_id = 0; ++#if MT_PROTOCOL_B ++ uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0}; ++#endif /* MT_PROTOCOL_B */ ++ int32_t i = 0; ++ int32_t finger_cnt = 0; ++ uint8_t pen_format_id = 0; ++ uint32_t pen_x = 0; ++ uint32_t pen_y = 0; ++ uint32_t pen_pressure = 0; ++ uint32_t pen_distance = 0; ++ int8_t pen_tilt_x = 0; ++ int8_t pen_tilt_y = 0; ++ uint32_t pen_btn1 = 0; ++ uint32_t pen_btn2 = 0; ++ uint32_t pen_battery = 0; ++ ++ mutex_lock(&ts->lock); ++ ++ if (ts->dev_pm_suspend) { ++ ret = wait_for_completion_timeout(&ts->dev_pm_suspend_completion, msecs_to_jiffies(500)); ++ if (!ret) { ++ NVT_ERR("system(spi) can't finished resuming procedure, skip it\n"); ++ goto XFER_ERROR; ++ } ++ } ++ ++ if (ts->pen_support) ++ ret = CTP_SPI_READ(ts->client, point_data, POINT_DATA_LEN + PEN_DATA_LEN + 1); ++ else ++ ret = CTP_SPI_READ(ts->client, point_data, POINT_DATA_LEN + 1); ++ if (ret < 0) { ++ NVT_ERR("CTP_SPI_READ failed.(%d)\n", ret); ++ goto XFER_ERROR; ++ } ++ ++ /*--- dump SPI buf --- ++ for (i = 0; i < 10; i++) { ++ NVT_LOG("%02X %02X %02X %02X %02X %02X \n", ++ point_data[1+i*6], point_data[2+i*6], point_data[3+i*6], point_data[4+i*6], point_data[5+i*6], point_data[6+i*6]); ++ } ++ */ ++ ++#if NVT_TOUCH_WDT_RECOVERY ++ /* ESD protect by WDT */ ++ if (nvt_wdt_fw_recovery(point_data)) { ++ NVT_ERR("Recover for fw reset, %02X\n", point_data[1]); ++ if (point_data[1] == 0xFD) { ++ NVT_ERR("Dump FW history:\n"); ++ nvt_dump_fw_history(); ++ } ++ nvt_update_firmware(ts->fw_name); ++ goto XFER_ERROR; ++ } ++#endif /* #if NVT_TOUCH_WDT_RECOVERY */ ++ ++ /* ESD protect by FW handshake */ ++ if (nvt_fw_recovery(point_data)) { ++#if NVT_TOUCH_ESD_PROTECT ++ nvt_esd_check_enable(true); ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ goto XFER_ERROR; ++ } ++ ++ finger_cnt = 0; ++ ++ for (i = 0; i < ts->max_touch_num; i++) { ++ position = 1 + 6 * i; ++ input_id = (uint8_t)(point_data[position + 0] >> 3); ++ if ((input_id == 0) || (input_id > ts->max_touch_num)) ++ continue; ++ ++ if (((point_data[position] & 0x07) == 0x01) || ((point_data[position] & 0x07) == 0x02)) { //finger down (enter & moving) ++#if NVT_TOUCH_ESD_PROTECT ++ /* update interrupt timer */ ++ irq_timer = jiffies; ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ input_x = (uint32_t)(point_data[position + 1] << 4) + (uint32_t) (point_data[position + 3] >> 4); ++ input_y = (uint32_t)(point_data[position + 2] << 4) + (uint32_t) (point_data[position + 3] & 0x0F); ++ if ((input_x < 0) || (input_y < 0)) ++ continue; ++ if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) ++ continue; ++ input_w = (uint32_t)(point_data[position + 4]); ++ if (input_w == 0) ++ input_w = 1; ++ if (i < 2) { ++ input_p = (uint32_t)(point_data[position + 5]) + (uint32_t)(point_data[i + 63] << 8); ++ if (input_p > TOUCH_FORCE_NUM) ++ input_p = TOUCH_FORCE_NUM; ++ } else { ++ input_p = (uint32_t)(point_data[position + 5]); ++ } ++ if (input_p == 0) ++ input_p = 1; ++ ++#if MT_PROTOCOL_B ++ press_id[input_id - 1] = 1; ++ input_mt_slot(ts->input_dev, input_id - 1); ++ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); ++#else /* MT_PROTOCOL_B */ ++ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1); ++ input_report_key(ts->input_dev, BTN_TOUCH, 1); ++#endif /* MT_PROTOCOL_B */ ++ ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); ++ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); ++ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); ++ ++#if MT_PROTOCOL_B ++#else /* MT_PROTOCOL_B */ ++ input_mt_sync(ts->input_dev); ++#endif /* MT_PROTOCOL_B */ ++ ++ finger_cnt++; ++ } ++ } ++ ++#if MT_PROTOCOL_B ++ for (i = 0; i < ts->max_touch_num; i++) { ++ if (press_id[i] != 1) { ++ input_mt_slot(ts->input_dev, i); ++ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); ++ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); ++ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); ++ } ++ } ++ input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); ++#else /* MT_PROTOCOL_B */ ++ if (finger_cnt == 0) { ++ input_report_key(ts->input_dev, BTN_TOUCH, 0); ++ input_mt_sync(ts->input_dev); ++ } ++#endif /* MT_PROTOCOL_B */ ++ ++#if TOUCH_KEY_NUM > 0 ++ if (point_data[61] == 0xF8) { ++#if NVT_TOUCH_ESD_PROTECT ++ /* update interrupt timer */ ++ irq_timer = jiffies; ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ for (i = 0; i < ts->max_button_num; i++) { ++ input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01)); ++ } ++ } else { ++ for (i = 0; i < ts->max_button_num; i++) { ++ input_report_key(ts->input_dev, touch_key_array[i], 0); ++ } ++ } ++#endif ++ ++ input_sync(ts->input_dev); ++ ++ if (ts->pen_support && ts->pen_input_dev_enable && !(ts->pen_is_charge)) { ++/* ++ //--- dump pen buf --- ++ printk("%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", ++ point_data[66], point_data[67], point_data[68], point_data[69], point_data[70], ++ point_data[71], point_data[72], point_data[73], point_data[74], point_data[75], ++ point_data[76], point_data[77], point_data[78], point_data[79]); ++*/ ++ // parse and handle pen report ++ pen_format_id = point_data[66]; ++ if (pen_format_id != 0xFF) { ++ if (pen_format_id == 0x01) { ++ // report pen data ++ pen_x = (uint32_t)(point_data[67] << 8) + (uint32_t)(point_data[68]); ++ pen_y = (uint32_t)(point_data[69] << 8) + (uint32_t)(point_data[70]); ++ if (pen_x >= ts->abs_x_max * 2 - 1) { ++ pen_x -= 1; ++ } ++ if (pen_y >= ts->abs_y_max * 2 - 1) { ++ pen_y -= 1; ++ } ++ pen_pressure = (uint32_t)(point_data[71] << 8) + (uint32_t)(point_data[72]); ++ pen_tilt_x = (int32_t)point_data[73]; ++ pen_tilt_y = (int32_t)point_data[74]; ++ pen_distance = (uint32_t)(point_data[75] << 8) + (uint32_t)(point_data[76]); ++ pen_btn1 = (uint32_t)(point_data[77] & 0x01); ++ pen_btn2 = (uint32_t)((point_data[77] >> 1) & 0x01); ++ pen_battery = (uint32_t)point_data[78]; ++// printk("x=%d,y=%d,p=%d,tx=%d,ty=%d,d=%d,b1=%d,b2=%d,bat=%d\n", pen_x, pen_y, pen_pressure, ++// pen_tilt_x, pen_tilt_y, pen_distance, pen_btn1, pen_btn2, pen_battery); ++ ++ input_report_abs(ts->pen_input_dev, ABS_X, pen_x); ++ input_report_abs(ts->pen_input_dev, ABS_Y, pen_y); ++ input_report_abs(ts->pen_input_dev, ABS_PRESSURE, pen_pressure); ++ input_report_key(ts->pen_input_dev, BTN_TOUCH, !!pen_pressure); ++ input_report_abs(ts->pen_input_dev, ABS_TILT_X, pen_tilt_x); ++ input_report_abs(ts->pen_input_dev, ABS_TILT_Y, pen_tilt_y); ++ input_report_abs(ts->pen_input_dev, ABS_DISTANCE, pen_distance); ++ input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, !!pen_distance || !!pen_pressure); ++ input_report_key(ts->pen_input_dev, BTN_STYLUS, pen_btn1); ++ input_report_key(ts->pen_input_dev, BTN_STYLUS2, pen_btn2); ++ input_sync(ts->pen_input_dev); ++ // TBD: pen battery event report ++ // NVT_LOG("pen_battery=%d\n", pen_battery); ++ } else if (pen_format_id == 0xF0) { ++ // report Pen ID ++ } else { ++ NVT_ERR("Unknown pen format id!\n"); ++ goto XFER_ERROR; ++ } ++ } else { // pen_format_id = 0xFF, i.e. no pen present ++ release_pen_event(); ++ } ++ } /* if (ts->pen_support) */ ++ ++XFER_ERROR: ++ ++ mutex_unlock(&ts->lock); ++ return IRQ_HANDLED; ++} ++ ++ ++/******************************************************* ++Description: ++ Novatek touchscreen check chip version trim function. ++ ++return: ++ Executive outcomes. 0---NVT IC. -1---not NVT IC. ++*******************************************************/ ++static int8_t nvt_ts_check_chip_ver_trim(uint32_t chip_ver_trim_addr) ++{ ++ ++ ts->mmap = &NT36523_memory_map; ++ ts->carrier_system = NT36523_hw_info.carrier_system; ++ ts->hw_crc = NT36523_hw_info.hw_crc; ++ return 0; ++} ++ ++static void nvt_suspend_work(struct work_struct *work) ++{ ++ struct nvt_ts_data *ts_core = container_of(work, struct nvt_ts_data, suspend_work); ++ nvt_ts_suspend(&ts_core->client->dev); ++} ++ ++static void nvt_resume_work(struct work_struct *work) ++{ ++ struct nvt_ts_data *ts_core = container_of(work, struct nvt_ts_data, resume_work); ++ nvt_ts_resume(&ts_core->client->dev); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen driver probe function. ++ ++return: ++ Executive outcomes. 0---succeed. negative---failed ++*******************************************************/ ++static int32_t nvt_ts_probe(struct spi_device *client) ++{ ++ int32_t ret = 0; ++#if (TOUCH_KEY_NUM > 0) ++ int32_t retry = 0; ++#endif ++ ++ NVT_LOG("probe start\n"); ++ ++ ts = kzalloc(sizeof(struct nvt_ts_data), GFP_KERNEL); ++ if (ts == NULL) { ++ NVT_ERR("failed to allocated memory for nvt ts data\n"); ++ return -ENOMEM; ++ } ++ ++ ts->xbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN+1+DUMMY_BYTES), GFP_KERNEL); ++ if(ts->xbuf == NULL) { ++ NVT_ERR("kzalloc for xbuf failed!\n"); ++ ret = -ENOMEM; ++ goto err_malloc_xbuf; ++ } ++ ++ ts->rbuf = (uint8_t *)kzalloc(NVT_READ_LEN, GFP_KERNEL); ++ if(ts->rbuf == NULL) { ++ NVT_ERR("kzalloc for rbuf failed!\n"); ++ ret = -ENOMEM; ++ goto err_malloc_rbuf; ++ } ++ ++ ts->client = client; ++ spi_set_drvdata(client, ts); ++ ++ //---prepare for spi parameter--- ++ if (ts->client->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { ++ NVT_ERR("Full duplex not supported by master\n"); ++ ret = -EIO; ++ goto err_ckeck_full_duplex; ++ } ++ ts->client->bits_per_word = 8; ++ ts->client->mode = SPI_MODE_0; ++ ++ ret = spi_setup(ts->client); ++ if (ret < 0) { ++ NVT_ERR("Failed to perform SPI setup\n"); ++ goto err_spi_setup; ++ } ++ ++#ifdef CONFIG_MTK_SPI ++ /* old usage of MTK spi API */ ++ memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mt_chip_conf)); ++ ts->client->controller_data = (void *)&ts->spi_ctrl; ++#endif ++ ++#ifdef CONFIG_SPI_MT65XX ++ /* new usage of MTK spi API */ ++ memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mtk_chip_config)); ++ ts->client->controller_data = (void *)&ts->spi_ctrl; ++#endif ++ ++ NVT_LOG("mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz); ++ ++ //---parse dts--- ++ ret = nvt_parse_dt(&client->dev); ++ if (ret) { ++ NVT_ERR("parse dt error\n"); ++ goto err_spi_setup; ++ } ++ ++ //---request and config GPIOs--- ++ ret = nvt_gpio_config(ts); ++ if (ret) { ++ NVT_ERR("gpio config error!\n"); ++ goto err_gpio_config_failed; ++ } ++ ++ mutex_init(&ts->lock); ++ mutex_init(&ts->xbuf_lock); ++ ++ //---eng reset before TP_RESX high ++ nvt_eng_reset(); ++ ++#if NVT_TOUCH_SUPPORT_HW_RST ++ gpio_set_value(ts->reset_gpio, 1); ++#endif ++ ++ // need 10ms delay after POR(power on reset) ++ msleep(10); ++ ++ //---check chip version trim--- ++ ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR); ++ if (ret) { ++ NVT_LOG("try to check from old chip ver trim address\n"); ++ ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_OLD_ADDR); ++ if (ret) { ++ NVT_ERR("chip is not identified\n"); ++ ret = -EINVAL; ++ goto err_chipvertrim_failed; ++ } ++ } ++ ++ ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; ++ ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; ++ ++ //---allocate input device--- ++ ts->input_dev = input_allocate_device(); ++ if (ts->input_dev == NULL) { ++ NVT_ERR("allocate input device failed\n"); ++ ret = -ENOMEM; ++ goto err_input_dev_alloc_failed; ++ } ++ ++ ts->max_touch_num = TOUCH_MAX_FINGER_NUM; ++ ++#if TOUCH_KEY_NUM > 0 ++ ts->max_button_num = TOUCH_KEY_NUM; ++#endif ++ ++ ts->int_trigger_type = INT_TRIGGER_TYPE; ++ ++ //---set input device info.--- ++ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); ++ ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); ++ ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); ++ ++ ts->db_wakeup = 0; ++ ts->fw_ver = 0; ++ ts->x_num = 32; ++ ts->y_num = 50; ++ ts->x_gang_num = 4; ++ ts->y_gang_num = 6; ++ ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; ++ ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; ++ ts->max_button_num = TOUCH_KEY_NUM; ++ NVT_LOG("Set default fw_ver=%d, x_num=%d, y_num=%d, " ++ "abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n", ++ ts->fw_ver, ts->x_num, ts->y_num, ++ ts->abs_x_max, ts->abs_y_max, ts->max_button_num); ++ ++#if MT_PROTOCOL_B ++ input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0); ++#endif ++ ++ input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, TOUCH_FORCE_NUM, 0, 0); //pressure = TOUCH_FORCE_NUM ++ ++#if TOUCH_MAX_FINGER_NUM > 1 ++ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //area = 255 ++ ++ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max - 1, 0, 0); ++ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max - 1, 0, 0); ++#if MT_PROTOCOL_B ++ // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it ++#else ++ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); ++#endif //MT_PROTOCOL_B ++#endif //TOUCH_MAX_FINGER_NUM > 1 ++ ++#if TOUCH_KEY_NUM > 0 ++ for (retry = 0; retry < ts->max_button_num; retry++) { ++ input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]); ++ } ++#endif ++ ++ sprintf(ts->phys, "input/ts"); ++ ts->input_dev->name = NVT_TS_NAME; ++ ts->input_dev->phys = ts->phys; ++ ts->input_dev->id.bustype = BUS_SPI; ++ ++ //---register input device--- ++ ret = input_register_device(ts->input_dev); ++ if (ret) { ++ NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret); ++ goto err_input_register_device_failed; ++ } ++ ++ if (ts->pen_support) { ++ //---allocate pen input device--- ++ ts->pen_input_dev = input_allocate_device(); ++ if (ts->pen_input_dev == NULL) { ++ NVT_ERR("allocate pen input device failed\n"); ++ ret = -ENOMEM; ++ goto err_pen_input_dev_alloc_failed; ++ } ++ ++ //---set pen input device info.--- ++ ts->pen_input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); ++ ts->pen_input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); ++ ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); ++ //ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); ++ ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); ++ ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); ++ ts->pen_input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); ++ ++ if (ts->wgp_stylus) { ++ input_set_abs_params(ts->pen_input_dev, ABS_X, 0, ts->abs_x_max * 2 - 1, 0, 0); ++ input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, ts->abs_y_max * 2 - 1, 0, 0); ++ } else { ++ input_set_abs_params(ts->pen_input_dev, ABS_X, 0, ts->abs_x_max - 1, 0, 0); ++ input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, ts->abs_y_max - 1, 0, 0); ++ } ++ ++ input_set_abs_params(ts->pen_input_dev, ABS_PRESSURE, 0, PEN_PRESSURE_MAX, 0, 0); ++ input_set_abs_params(ts->pen_input_dev, ABS_DISTANCE, 0, PEN_DISTANCE_MAX, 0, 0); ++ input_set_abs_params(ts->pen_input_dev, ABS_TILT_X, PEN_TILT_MIN, PEN_TILT_MAX, 0, 0); ++ input_set_abs_params(ts->pen_input_dev, ABS_TILT_Y, PEN_TILT_MIN, PEN_TILT_MAX, 0, 0); ++ ++ sprintf(ts->pen_phys, "input/pen"); ++ ts->pen_input_dev->name = NVT_PEN_NAME; ++ ts->pen_input_dev->phys = ts->pen_phys; ++ ts->pen_input_dev->id.bustype = BUS_SPI; ++ ++ //---register pen input device--- ++ ret = input_register_device(ts->pen_input_dev); ++ if (ret) { ++ NVT_ERR("register pen input device (%s) failed. ret=%d\n", ts->pen_input_dev->name, ret); ++ goto err_pen_input_register_device_failed; ++ } ++ } /* if (ts->pen_support) */ ++ ++ //---set int-pin & request irq--- ++ client->irq = gpio_to_irq(ts->irq_gpio); ++ if (client->irq) { ++ NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type); ++ ts->irq_enabled = true; ++ ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func, ++ ts->int_trigger_type | IRQF_ONESHOT, NVT_SPI_NAME, ts); ++ if (ret != 0) { ++ NVT_ERR("request irq failed. ret=%d\n", ret); ++ goto err_int_request_failed; ++ } else { ++ nvt_irq_enable(false); ++ NVT_LOG("request irq %d succeed\n", client->irq); ++ } ++ } ++ ++ ts->pen_is_charge = false; ++ ++ ts->lkdown_readed =false; ++ pm_stay_awake(&client->dev); ++ ++ ts->ic_state = NVT_IC_INIT; ++ ts->dev_pm_suspend = false; ++ ts->gesture_command_delayed = -1; ++ init_completion(&ts->dev_pm_suspend_completion); ++ ts->fw_debug = false; ++ ++#ifdef CONFIG_FACTORY_BUILD ++ ts->pen_input_dev_enable = 1; ++#else ++ ts->pen_input_dev_enable = 0; ++#endif ++ ++#if BOOT_UPDATE_FIRMWARE ++ nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); ++ if (!nvt_fwu_wq) { ++ NVT_ERR("nvt_fwu_wq create workqueue failed\n"); ++ ret = -ENOMEM; ++ goto err_create_nvt_fwu_wq_failed; ++ } ++ INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware); ++ // please make sure boot update start after display reset(RESX) sequence ++ queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000)); ++#endif ++ ++ NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT); ++#if NVT_TOUCH_ESD_PROTECT ++ INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func); ++ nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1); ++ if (!nvt_esd_check_wq) { ++ NVT_ERR("nvt_esd_check_wq create workqueue failed\n"); ++ ret = -ENOMEM; ++ goto err_create_nvt_esd_check_wq_failed; ++ } ++ queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, ++ msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++ ts->event_wq = alloc_workqueue("nvt-event-queue", ++ WQ_UNBOUND | WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1); ++ if (!ts->event_wq) { ++ NVT_ERR("Can not create work thread for suspend/resume!!"); ++ ret = -ENOMEM; ++ goto err_alloc_work_thread_failed; ++ } ++ INIT_WORK(&ts->resume_work, nvt_resume_work); ++ INIT_WORK(&ts->suspend_work, nvt_suspend_work); ++ ++#ifdef CONFIG_DRM ++ ts->drm_notif.notifier_call = nvt_drm_notifier_callback; ++ ret = mi_drm_register_client(&ts->drm_notif); ++ if(ret) { ++ NVT_ERR("register drm_notifier failed. ret=%d\n", ret); ++ goto err_register_drm_notif_failed; ++ } ++#endif ++ ++ bTouchIsAwake = 1; ++ NVT_LOG("end\n"); ++ ++ nvt_irq_enable(true); ++ ++ return 0; ++ ++#ifdef CONFIG_DRM ++ if (mi_drm_unregister_client(&ts->drm_notif)) ++ NVT_ERR("Error occurred while unregistering drm_notifier.\n"); ++err_register_drm_notif_failed: ++#endif ++ ++err_alloc_work_thread_failed: ++ ++#if NVT_TOUCH_ESD_PROTECT ++ if (nvt_esd_check_wq) { ++ cancel_delayed_work_sync(&nvt_esd_check_work); ++ destroy_workqueue(nvt_esd_check_wq); ++ nvt_esd_check_wq = NULL; ++ } ++err_create_nvt_esd_check_wq_failed: ++#endif ++#if BOOT_UPDATE_FIRMWARE ++ if (nvt_fwu_wq) { ++ cancel_delayed_work_sync(&ts->nvt_fwu_work); ++ destroy_workqueue(nvt_fwu_wq); ++ nvt_fwu_wq = NULL; ++ } ++err_create_nvt_fwu_wq_failed: ++ ++#endif ++ free_irq(client->irq, ts); ++err_int_request_failed: ++ if (ts->pen_support) { ++ input_unregister_device(ts->pen_input_dev); ++ ts->pen_input_dev = NULL; ++ } ++err_pen_input_register_device_failed: ++ if (ts->pen_support) { ++ if (ts->pen_input_dev) { ++ input_free_device(ts->pen_input_dev); ++ ts->pen_input_dev = NULL; ++ } ++ } ++err_pen_input_dev_alloc_failed: ++ input_unregister_device(ts->input_dev); ++ ts->input_dev = NULL; ++err_input_register_device_failed: ++ if (ts->input_dev) { ++ input_free_device(ts->input_dev); ++ ts->input_dev = NULL; ++ } ++err_input_dev_alloc_failed: ++err_chipvertrim_failed: ++ mutex_destroy(&ts->xbuf_lock); ++ mutex_destroy(&ts->lock); ++ nvt_gpio_deconfig(ts); ++err_gpio_config_failed: ++err_spi_setup: ++err_ckeck_full_duplex: ++ spi_set_drvdata(client, NULL); ++ if (ts->rbuf) { ++ kfree(ts->rbuf); ++ ts->rbuf = NULL; ++ } ++err_malloc_rbuf: ++ if (ts->xbuf) { ++ kfree(ts->xbuf); ++ ts->xbuf = NULL; ++ } ++err_malloc_xbuf: ++ if (ts) { ++ kfree(ts); ++ ts = NULL; ++ } ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen driver release function. ++ ++return: ++ Executive outcomes. 0---succeed. ++*******************************************************/ ++static void nvt_ts_remove(struct spi_device *client) ++{ ++ NVT_LOG("Removing driver...\n"); ++ ++#ifdef CONFIG_DRM ++ if (mi_drm_unregister_client(&ts->drm_notif)) ++ NVT_ERR("Error occurred while unregistering drm_notifier.\n"); ++#endif ++ ++#if NVT_TOUCH_ESD_PROTECT ++ if (nvt_esd_check_wq) { ++ cancel_delayed_work_sync(&nvt_esd_check_work); ++ nvt_esd_check_enable(false); ++ destroy_workqueue(nvt_esd_check_wq); ++ nvt_esd_check_wq = NULL; ++ } ++#endif ++ ++#if BOOT_UPDATE_FIRMWARE ++ if (nvt_fwu_wq) { ++ cancel_delayed_work_sync(&ts->nvt_fwu_work); ++ destroy_workqueue(nvt_fwu_wq); ++ nvt_fwu_wq = NULL; ++ } ++#endif ++ ++ nvt_irq_enable(false); ++ free_irq(client->irq, ts); ++ ++ mutex_destroy(&ts->xbuf_lock); ++ mutex_destroy(&ts->lock); ++ ++ nvt_gpio_deconfig(ts); ++ ++ if (ts->pen_support) { ++ if (ts->pen_input_dev) { ++ input_unregister_device(ts->pen_input_dev); ++ ts->pen_input_dev = NULL; ++ } ++ } ++ ++ if (ts->input_dev) { ++ input_unregister_device(ts->input_dev); ++ ts->input_dev = NULL; ++ } ++ ++ spi_set_drvdata(client, NULL); ++ ++ if (ts) { ++ kfree(ts); ++ ts = NULL; ++ } ++} ++ ++static void nvt_ts_shutdown(struct spi_device *client) ++{ ++ NVT_LOG("Shutdown driver...\n"); ++ ++ nvt_irq_enable(false); ++ ++#ifdef CONFIG_DRM ++ if (mi_drm_unregister_client(&ts->drm_notif)) ++ NVT_ERR("Error occurred while unregistering drm_notifier.\n"); ++#endif ++ ++ destroy_workqueue(ts->event_wq); ++ ++#if NVT_TOUCH_ESD_PROTECT ++ if (nvt_esd_check_wq) { ++ cancel_delayed_work_sync(&nvt_esd_check_work); ++ nvt_esd_check_enable(false); ++ destroy_workqueue(nvt_esd_check_wq); ++ nvt_esd_check_wq = NULL; ++ } ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++#if BOOT_UPDATE_FIRMWARE ++ if (nvt_fwu_wq) { ++ cancel_delayed_work_sync(&ts->nvt_fwu_work); ++ destroy_workqueue(nvt_fwu_wq); ++ nvt_fwu_wq = NULL; ++ } ++#endif ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen driver suspend function. ++ ++return: ++ Executive outcomes. 0---succeed. ++*******************************************************/ ++static int32_t nvt_ts_suspend(struct device *dev) ++{ ++ uint8_t buf[4] = {0}; ++#if MT_PROTOCOL_B ++ uint32_t i = 0; ++#endif ++ ++ if (!bTouchIsAwake) { ++ NVT_LOG("Touch is already suspend\n"); ++ return 0; ++ } ++ ++ pm_stay_awake(dev); ++ ts->ic_state = NVT_IC_SUSPEND_IN; ++ ++ if (!ts->db_wakeup) { ++ if (!ts->irq_enabled) ++ NVT_LOG("IRQ already disabled\n"); ++ else ++ nvt_irq_enable(false); ++ } ++ ++#if NVT_TOUCH_ESD_PROTECT ++ NVT_LOG("cancel delayed work sync\n"); ++ cancel_delayed_work_sync(&nvt_esd_check_work); ++ nvt_esd_check_enable(false); ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++ mutex_lock(&ts->lock); ++ ++ NVT_LOG("suspend start\n"); ++ ++ bTouchIsAwake = 0; ++ ++ if (ts->pen_input_dev_enable) { ++ NVT_LOG("if enable pen,will close it"); ++ } ++ ++ if (ts->db_wakeup) { ++ /*---write command to enter "wakeup gesture mode"---*/ ++ /*DoubleClick wakeup CMD was sent by display to meet timing*/ ++ /* ++ buf[0] = EVENT_MAP_HOST_CMD; ++ buf[1] = 0x13; ++ CTP_SPI_WRITE(ts->client, buf, 2); ++ */ ++ enable_irq_wake(ts->client->irq); ++ ++ NVT_LOG("Enabled touch wakeup gesture\n"); ++ } else { ++ /*---write command to enter "deep sleep mode"---*/ ++ buf[0] = EVENT_MAP_HOST_CMD; ++ buf[1] = 0x11; ++ CTP_SPI_WRITE(ts->client, buf, 2); ++ } ++ ++ mutex_unlock(&ts->lock); ++ ++ /* release all touches */ ++#if MT_PROTOCOL_B ++ for (i = 0; i < ts->max_touch_num; i++) { ++ input_mt_slot(ts->input_dev, i); ++ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); ++ input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); ++ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); ++ } ++#endif ++ input_report_key(ts->input_dev, BTN_TOUCH, 0); ++#if !MT_PROTOCOL_B ++ input_mt_sync(ts->input_dev); ++#endif ++ input_sync(ts->input_dev); ++ ++ msleep(50); ++ /* release pen event */ ++ release_pen_event(); ++ if (likely(ts->ic_state == NVT_IC_SUSPEND_IN)) ++ ts->ic_state = NVT_IC_SUSPEND_OUT; ++ else ++ NVT_ERR("IC state may error,caused by suspend/resume flow, please CHECK!!"); ++ pm_relax(dev); ++ NVT_LOG("end\n"); ++ ++ return 0; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen driver resume function. ++ ++return: ++ Executive outcomes. 0---succeed. ++*******************************************************/ ++static int32_t nvt_ts_resume(struct device *dev) ++{ ++ int ret = 0; ++ if (bTouchIsAwake) { ++ NVT_LOG("Touch is already resume\n"); ++ return 0; ++ } ++ ++ if (ts->dev_pm_suspend) ++ pm_stay_awake(dev); ++ ++ mutex_lock(&ts->lock); ++ ++ NVT_LOG("resume start\n"); ++ ts->ic_state = NVT_IC_RESUME_IN; ++ ++ // please make sure display reset(RESX) sequence and mipi dsi cmds sent before this ++#if NVT_TOUCH_SUPPORT_HW_RST ++ gpio_set_value(ts->reset_gpio, 1); ++#endif ++ ret = nvt_update_firmware(ts->fw_name); ++ if (ret) ++ NVT_ERR("download firmware failed\n"); ++ ++ nvt_check_fw_reset_state(RESET_STATE_REK); ++ ++ if (!ts->db_wakeup && !ts->irq_enabled) { ++ nvt_irq_enable(true); ++ } ++ ++#if NVT_TOUCH_ESD_PROTECT ++ nvt_esd_check_enable(false); ++ queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, ++ msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++ bTouchIsAwake = 1; ++ ++ mutex_unlock(&ts->lock); ++ ++ if (likely(ts->ic_state == NVT_IC_RESUME_IN)) { ++ ts->ic_state = NVT_IC_RESUME_OUT; ++ } else { ++ NVT_ERR("IC state may error,caused by suspend/resume flow, please CHECK!!"); ++ } ++ if (ts->gesture_command_delayed >= 0){ ++ ts->db_wakeup = ts->gesture_command_delayed; ++ ts->gesture_command_delayed = -1; ++ NVT_LOG("execute delayed command, set double click wakeup %d\n", ts->db_wakeup); ++ } ++ ++ if (ts->dev_pm_suspend) ++ pm_relax(dev); ++ NVT_LOG("end\n"); ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_DRM ++static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) ++{ ++ int blank = *(enum drm_notifier_data *)data; ++ struct nvt_ts_data *ts_data = ++ container_of(self, struct nvt_ts_data, drm_notif); ++ ++ if (data && ts_data) { ++ if (event == MI_DRM_EARLY_EVENT_BLANK) { ++ if (blank == MI_DRM_BLANK_POWERDOWN) { ++ NVT_LOG("event=%lu, *blank=%d\n", event, blank); ++ flush_workqueue(ts_data->event_wq); ++ queue_work(ts_data->event_wq, &ts_data->suspend_work); ++ } ++ } else if (event == MI_DRM_EVENT_BLANK) { ++ if (blank == MI_DRM_BLANK_UNBLANK) { ++ NVT_LOG("event=%lu, *blank=%d\n", event, blank); ++ flush_workqueue(ts_data->event_wq); ++ queue_work(ts_data->event_wq, &ts_data->resume_work); ++ } ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static int nvt_pm_suspend(struct device *dev) ++{ ++ if (device_may_wakeup(dev) && ts->db_wakeup) { ++ NVT_LOG("enable touch irq wake\n"); ++ enable_irq_wake(ts->client->irq); ++ } ++ ts->dev_pm_suspend = true; ++ reinit_completion(&ts->dev_pm_suspend_completion); ++ ++ return 0; ++} ++ ++static int nvt_pm_resume(struct device *dev) ++{ ++ if (device_may_wakeup(dev) && ts->db_wakeup) { ++ NVT_LOG("disable touch irq wake\n"); ++ disable_irq_wake(ts->client->irq); ++ } ++ ts->dev_pm_suspend = false; ++ complete(&ts->dev_pm_suspend_completion); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops nvt_dev_pm_ops = { ++ .suspend = nvt_pm_suspend, ++ .resume = nvt_pm_resume, ++}; ++ ++static const struct spi_device_id nvt_ts_id[] = { ++ { NVT_SPI_NAME, 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(spi, nvt_ts_id); ++ ++#ifdef CONFIG_OF ++static struct of_device_id nvt_match_table[] = { ++ { .compatible = "novatek,NVT-ts-spi",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, nvt_match_table); ++#endif ++ ++static struct spi_driver nvt_spi_driver = { ++ .probe = nvt_ts_probe, ++ .remove = nvt_ts_remove, ++ .shutdown = nvt_ts_shutdown, ++ .id_table = nvt_ts_id, ++ .driver = { ++ .name = NVT_SPI_NAME, ++ .owner = THIS_MODULE, ++#ifdef CONFIG_PM ++ .pm = &nvt_dev_pm_ops, ++#endif ++#ifdef CONFIG_OF ++ .of_match_table = nvt_match_table, ++#endif ++ }, ++}; ++ ++module_spi_driver(nvt_spi_driver); ++ ++MODULE_DESCRIPTION("Novatek Touchscreen Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.h b/drivers/input/touchscreen/nt36523/nt36xxx.h +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.h +@@ -0,0 +1,242 @@ ++/* ++ * Copyright (C) 2010 - 2018 Novatek, Inc. ++ * Copyright (C) 2021 XiaoMi, Inc. ++ * ++ * $Revision: 69262 $ ++ * $Date: 2020-09-23 15:07:14 +0800 (週三, 23 九月 2020) $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++#ifndef _LINUX_NVT_TOUCH_H ++#define _LINUX_NVT_TOUCH_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "nt36xxx_mem_map.h" ++ ++#define NVT_DEBUG 1 ++ ++//---GPIO number--- ++#define NVTTOUCH_RST_PIN 980 ++#define NVTTOUCH_INT_PIN 943 ++ ++#define PINCTRL_STATE_ACTIVE "pmx_ts_active" ++#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" ++ ++//---INT trigger mode--- ++//#define IRQ_TYPE_EDGE_RISING 1 ++//#define IRQ_TYPE_EDGE_FALLING 2 ++#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING ++ ++ ++//---SPI driver info.--- ++#define NVT_SPI_NAME "NVT-ts-spi" ++ ++#if NVT_DEBUG ++#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) ++#else ++#define NVT_LOG(fmt, args...) pr_info("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) ++#endif ++#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) ++ ++//---Input device info.--- ++#define NVT_TS_NAME "NVTCapacitiveTouchScreen" ++#define NVT_PEN_NAME "NVTCapacitivePen" ++ ++//---Touch info.--- ++#define TOUCH_DEFAULT_MAX_WIDTH 1600 ++#define TOUCH_DEFAULT_MAX_HEIGHT 2560 ++#define TOUCH_MAX_FINGER_NUM 10 ++#define TOUCH_KEY_NUM 0 ++#if TOUCH_KEY_NUM > 0 ++extern const uint16_t touch_key_array[TOUCH_KEY_NUM]; ++#endif ++#define TOUCH_FORCE_NUM 1000 ++//---for Pen--- ++#define PEN_PRESSURE_MAX (4095) ++#define PEN_DISTANCE_MAX (1) ++#define PEN_TILT_MIN (-60) ++#define PEN_TILT_MAX (60) ++ ++/* Enable only when module have tp reset pin and connected to host */ ++#define NVT_TOUCH_SUPPORT_HW_RST 0 ++ ++//---Customerized func.--- ++#define NVT_TOUCH_MP 0 ++#define NVT_TOUCH_MP_SETTING_CRITERIA_FROM_CSV 0 ++#define MT_PROTOCOL_B 1 ++#define FUNCPAGE_PALM 4 ++#define PACKET_PALM_ON 3 ++#define PACKET_PALM_OFF 4 ++ ++#define BOOT_UPDATE_FIRMWARE 1 ++#define DEFAULT_BOOT_UPDATE_FIRMWARE_NAME "novatek/nt36523.bin" ++#define DEFAULT_MP_UPDATE_FIRMWARE_NAME "novatek_ts_mp.bin" ++ ++//---ESD Protect.--- ++#define NVT_TOUCH_ESD_PROTECT 1 ++#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ ++#define NVT_TOUCH_WDT_RECOVERY 1 ++ ++enum nvt_ic_state { ++ NVT_IC_SUSPEND_IN, ++ NVT_IC_SUSPEND_OUT, ++ NVT_IC_RESUME_IN, ++ NVT_IC_RESUME_OUT, ++ NVT_IC_INIT, ++}; ++ ++struct nvt_config_info { ++ u8 tp_vendor; ++ u8 tp_color; ++ u8 display_maker; ++ u8 glass_vendor; ++ const char *nvt_fw_name; ++ const char *nvt_mp_name; ++ const char *nvt_limit_name; ++}; ++ ++struct nvt_ts_data { ++ struct spi_device *client; ++ struct input_dev *input_dev; ++ struct delayed_work nvt_fwu_work; ++ struct work_struct switch_mode_work; ++ struct work_struct pen_charge_state_change_work; ++ bool pen_is_charge; ++ struct notifier_block pen_charge_state_notifier; ++ uint16_t addr; ++ int8_t phys[32]; ++#if defined(CONFIG_FB) ++#ifdef CONFIG_DRM ++ struct notifier_block drm_notif; ++#else ++ struct notifier_block fb_notif; ++#endif ++#endif ++ uint32_t config_array_size; ++ struct nvt_config_info *config_array; ++ const char *fw_name; ++ bool lkdown_readed; ++ uint8_t fw_ver; ++ uint8_t x_num; ++ uint8_t y_num; ++ int ic_state; ++ uint16_t abs_x_max; ++ uint16_t abs_y_max; ++ uint8_t max_touch_num; ++ uint8_t max_button_num; ++ uint32_t int_trigger_type; ++ int32_t irq_gpio; ++ uint32_t irq_flags; ++ int32_t reset_gpio; ++ uint32_t reset_flags; ++ struct mutex lock; ++ const struct nvt_ts_mem_map *mmap; ++ uint8_t carrier_system; ++ uint8_t hw_crc; ++ uint16_t nvt_pid; ++ uint8_t *rbuf; ++ uint8_t *xbuf; ++ struct mutex xbuf_lock; ++ bool irq_enabled; ++ uint8_t cascade; ++ bool pen_support; ++ bool wgp_stylus; ++ uint8_t x_gang_num; ++ uint8_t y_gang_num; ++ struct input_dev *pen_input_dev; ++ bool pen_input_dev_enable; ++ int8_t pen_phys[32]; ++ struct workqueue_struct *event_wq; ++ struct work_struct suspend_work; ++ struct work_struct resume_work; ++ int result_type; ++ int panel_index; ++ uint32_t spi_max_freq; ++ int db_wakeup; ++ uint8_t debug_flag; ++ bool fw_debug; ++ bool dev_pm_suspend; ++ struct completion dev_pm_suspend_completion; ++ bool palm_sensor_switch; ++ int gesture_command_delayed; ++ struct pinctrl *ts_pinctrl; ++ struct pinctrl_state *pinctrl_state_active; ++ struct pinctrl_state *pinctrl_state_suspend; ++}; ++ ++typedef enum { ++ RESET_STATE_INIT = 0xA0,// IC reset ++ RESET_STATE_REK, // ReK baseline ++ RESET_STATE_REK_FINISH, // baseline is ready ++ RESET_STATE_NORMAL_RUN, // normal run ++ RESET_STATE_MAX = 0xAF ++} RST_COMPLETE_STATE; ++ ++typedef enum { ++ EVENT_MAP_HOST_CMD = 0x50, ++ EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51, ++ EVENT_MAP_RESET_COMPLETE = 0x60, ++ EVENT_MAP_FWINFO = 0x78, ++ EVENT_MAP_PROJECTID = 0x9A, ++} SPI_EVENT_MAP; ++ ++//---SPI READ/WRITE--- ++#define SPI_WRITE_MASK(a) (a | 0x80) ++#define SPI_READ_MASK(a) (a & 0x7F) ++ ++#define DUMMY_BYTES (1) ++#define NVT_TRANSFER_LEN (63*1024) ++#define NVT_READ_LEN (2*1024) ++ ++typedef enum { ++ NVTWRITE = 0, ++ NVTREAD = 1 ++} NVT_SPI_RW; ++ ++//---extern structures--- ++extern struct nvt_ts_data *ts; ++ ++//---extern functions--- ++int32_t CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len); ++int32_t CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len); ++void nvt_bootloader_reset(void); ++void nvt_eng_reset(void); ++void nvt_sw_reset(void); ++void nvt_sw_reset_idle(void); ++void nvt_boot_ready(void); ++void nvt_bld_crc_enable(void); ++void nvt_fw_crc_enable(void); ++void nvt_tx_auto_copy_mode(void); ++void nvt_set_dbgfw_status(bool enable); ++void nvt_match_fw(void); ++int32_t nvt_update_firmware(const char *firmware_name); ++int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state); ++int32_t nvt_get_fw_info(void); ++int32_t nvt_clear_fw_status(void); ++int32_t nvt_check_fw_status(void); ++int32_t nvt_check_spi_dma_tx_info(void); ++int32_t nvt_set_page(uint32_t addr); ++int32_t nvt_write_addr(uint32_t addr, uint8_t data); ++int32_t nvt_read_pid(void); ++bool nvt_get_dbgfw_status(void); ++int32_t nvt_set_pocket_palm_switch(uint8_t pocket_palm_switch); ++void Boot_Update_Firmware(struct work_struct *work); ++#if NVT_TOUCH_ESD_PROTECT ++extern void nvt_esd_check_enable(uint8_t enable); ++#endif /* #if NVT_TOUCH_ESD_PROTECT */ ++ ++#endif /* _LINUX_NVT_TOUCH_H */ +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c b/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c +@@ -0,0 +1,857 @@ ++/* ++ * Copyright (C) 2010 - 2018 Novatek, Inc. ++ * Copyright (C) 2021 XiaoMi, Inc. ++ * ++ * $Revision: 68983 $ ++ * $Date: 2020-09-17 09:43:23 +0800 (週四, 17 九月 2020) $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#include ++#include ++ ++#include "nt36xxx.h" ++ ++#if BOOT_UPDATE_FIRMWARE ++ ++#define SIZE_4KB 4096 ++#define FLASH_SECTOR_SIZE SIZE_4KB ++#define FW_BIN_VER_OFFSET (fw_need_write_size - SIZE_4KB) ++#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1) ++#define NVT_FLASH_END_FLAG_LEN 3 ++#define NVT_FLASH_END_FLAG_ADDR (fw_need_write_size - NVT_FLASH_END_FLAG_LEN) ++ ++static ktime_t start, end; ++const struct firmware *fw_entry = NULL; ++static size_t fw_need_write_size = 0; ++static uint8_t *fwbuf = NULL; ++ ++struct nvt_ts_bin_map { ++ char name[12]; ++ uint32_t BIN_addr; ++ uint32_t SRAM_addr; ++ uint32_t size; ++ uint32_t crc; ++}; ++ ++static struct nvt_ts_bin_map *bin_map; ++ ++/******************************************************* ++Description: ++ Novatek touchscreen init variable and allocate buffer ++for download firmware function. ++ ++return: ++ n.a. ++*******************************************************/ ++static int32_t nvt_download_init(void) ++{ ++ /* allocate buffer for transfer firmware */ ++ //NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_TRANSFER_LEN); ++ ++ if (fwbuf == NULL) { ++ fwbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN + 1 + DUMMY_BYTES), GFP_KERNEL); ++ if(fwbuf == NULL) { ++ NVT_ERR("kzalloc for fwbuf failed!\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ return 0; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen checksum function. Calculate bin ++file checksum for comparison. ++ ++return: ++ n.a. ++*******************************************************/ ++static uint32_t CheckSum(const u8 *data, size_t len) ++{ ++ uint32_t i = 0; ++ uint32_t checksum = 0; ++ ++ for (i = 0 ; i < len+1 ; i++) ++ checksum += data[i]; ++ ++ checksum += len; ++ checksum = ~checksum +1; ++ ++ return checksum; ++} ++ ++static uint32_t byte_to_word(const uint8_t *data) ++{ ++ return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen parsing bin header function. ++ ++return: ++ n.a. ++*******************************************************/ ++static uint32_t partition = 0; ++static uint8_t ilm_dlm_num = 2; ++static uint8_t cascade_2nd_header_info = 0; ++static int32_t nvt_bin_header_parser(const u8 *fwdata, size_t fwsize) ++{ ++ uint32_t list = 0; ++ uint32_t pos = 0x00; ++ uint32_t end = 0x00; ++ uint8_t info_sec_num = 0; ++ uint8_t ovly_sec_num = 0; ++ uint8_t ovly_info = 0; ++ uint8_t find_bin_header = 0; ++ ++ /* Find the header size */ ++ end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24); ++ ++ /* check cascade next header */ ++ cascade_2nd_header_info = (fwdata[0x20] & 0x02) >> 1; ++ NVT_LOG("cascade_2nd_header_info = %d\n", cascade_2nd_header_info); ++ ++ if (cascade_2nd_header_info) { ++ pos = 0x30; // info section start at 0x30 offset ++ while (pos < (end / 2)) { ++ info_sec_num ++; ++ pos += 0x10; /* each header info is 16 bytes */ ++ } ++ ++ info_sec_num = info_sec_num + 1; //next header section ++ } else { ++ pos = 0x30; // info section start at 0x30 offset ++ while (pos < end) { ++ info_sec_num ++; ++ pos += 0x10; /* each header info is 16 bytes */ ++ } ++ } ++ ++ /* ++ * Find the DLM OVLY section ++ * [0:3] Overlay Section Number ++ * [4] Overlay Info ++ */ ++ ovly_info = (fwdata[0x28] & 0x10) >> 4; ++ ovly_sec_num = (ovly_info) ? (fwdata[0x28] & 0x0F) : 0; ++ ++ /* ++ * calculate all partition number ++ * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num ++ */ ++ partition = ilm_dlm_num + ovly_sec_num + info_sec_num; ++ NVT_LOG("ovly_info = %d, ilm_dlm_num = %d, ovly_sec_num = %d, info_sec_num = %d, partition = %d\n", ++ ovly_info, ilm_dlm_num, ovly_sec_num, info_sec_num, partition); ++ ++ /* allocated memory for header info */ ++ bin_map = (struct nvt_ts_bin_map *)kzalloc((partition+1) * sizeof(struct nvt_ts_bin_map), GFP_KERNEL); ++ if(bin_map == NULL) { ++ NVT_ERR("kzalloc for bin_map failed!\n"); ++ return -ENOMEM; ++ } ++ ++ for (list = 0; list < partition; list++) { ++ /* ++ * [1] parsing ILM & DLM header info ++ * BIN_addr : SRAM_addr : size (12-bytes) ++ * crc located at 0x18 & 0x1C ++ */ ++ if (list < ilm_dlm_num) { ++ bin_map[list].BIN_addr = byte_to_word(&fwdata[0 + list*12]); ++ bin_map[list].SRAM_addr = byte_to_word(&fwdata[4 + list*12]); ++ bin_map[list].size = byte_to_word(&fwdata[8 + list*12]); ++ if (ts->hw_crc) ++ bin_map[list].crc = byte_to_word(&fwdata[0x18 + list*4]); ++ else { //ts->hw_crc ++ if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) ++ bin_map[list].crc = CheckSum(&fwdata[bin_map[list].BIN_addr], bin_map[list].size); ++ else { ++ NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", ++ bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size); ++ return -EINVAL; ++ } ++ } //ts->hw_crc ++ if (list == 0) ++ sprintf(bin_map[list].name, "ILM"); ++ else if (list == 1) ++ sprintf(bin_map[list].name, "DLM"); ++ } ++ ++ /* ++ * [2] parsing others header info ++ * SRAM_addr : size : BIN_addr : crc (16-bytes) ++ */ ++ if ((list >= ilm_dlm_num) && (list < (ilm_dlm_num + info_sec_num))) { ++ if (find_bin_header == 0) { ++ /* others partition located at 0x30 offset */ ++ pos = 0x30 + (0x10 * (list - ilm_dlm_num)); ++ } else if (find_bin_header && cascade_2nd_header_info) { ++ /* cascade 2nd header info */ ++ pos = end - 0x10; ++ } ++ ++ bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]); ++ bin_map[list].size = byte_to_word(&fwdata[pos+4]); ++ bin_map[list].BIN_addr = byte_to_word(&fwdata[pos+8]); ++ if (ts->hw_crc) ++ bin_map[list].crc = byte_to_word(&fwdata[pos+12]); ++ else { //ts->hw_crc ++ if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) ++ bin_map[list].crc = CheckSum(&fwdata[bin_map[list].BIN_addr], bin_map[list].size); ++ else { ++ NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", ++ bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size); ++ return -EINVAL; ++ } ++ } //ts->hw_crc ++ /* detect header end to protect parser function */ ++ if ((bin_map[list].BIN_addr < end) && (bin_map[list].size != 0)) { ++ sprintf(bin_map[list].name, "Header"); ++ find_bin_header = 1; ++ } else { ++ sprintf(bin_map[list].name, "Info-%d", (list - ilm_dlm_num)); ++ } ++ } ++ ++ /* ++ * [3] parsing overlay section header info ++ * SRAM_addr : size : BIN_addr : crc (16-bytes) ++ */ ++ if (list >= (ilm_dlm_num + info_sec_num)) { ++ /* overlay info located at DLM (list = 1) start addr */ ++ pos = bin_map[1].BIN_addr + (0x10 * (list- ilm_dlm_num - info_sec_num)); ++ ++ bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]); ++ bin_map[list].size = byte_to_word(&fwdata[pos+4]); ++ bin_map[list].BIN_addr = byte_to_word(&fwdata[pos+8]); ++ if (ts->hw_crc) ++ bin_map[list].crc = byte_to_word(&fwdata[pos+12]); ++ else { //ts->hw_crc ++ if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) ++ bin_map[list].crc = CheckSum(&fwdata[bin_map[list].BIN_addr], bin_map[list].size); ++ else { ++ NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", ++ bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size); ++ return -EINVAL; ++ } ++ } //ts->hw_crc ++ sprintf(bin_map[list].name, "Overlay-%d", (list- ilm_dlm_num - info_sec_num)); ++ } ++ ++ /* BIN size error detect */ ++ if ((bin_map[list].BIN_addr + bin_map[list].size) > fwsize) { ++ NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", ++ bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size); ++ return -EINVAL; ++ } ++ ++// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X), CRC (0x%08X)\n", ++// list, bin_map[list].name, ++// bin_map[list].SRAM_addr, bin_map[list].size, bin_map[list].BIN_addr, bin_map[list].crc); ++ } ++ ++ return 0; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen release update firmware function. ++ ++return: ++ n.a. ++*******************************************************/ ++static void update_firmware_release(void) ++{ ++ if (fw_entry) { ++ release_firmware(fw_entry); ++ } ++ ++ fw_entry = NULL; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen request update firmware function. ++ ++return: ++ Executive outcomes. 0---succeed. -1,-22---failed. ++*******************************************************/ ++static int32_t update_firmware_request(const char *filename) ++{ ++ uint8_t retry = 0; ++ int32_t ret = 0; ++ ++ if (NULL == filename) { ++ return -ENOENT; ++ } ++ ++ while (1) { ++ NVT_LOG("filename is %s\n", filename); ++ ++ ret = request_firmware(&fw_entry, filename, &ts->client->dev); ++ if (ret) { ++ NVT_ERR("firmware load failed, ret=%d\n", ret); ++ goto request_fail; ++ } ++ ++ fw_need_write_size = fw_entry->size; ++ ++ // check if FW version add FW version bar equals 0xFF ++ if (*(fw_entry->data + FW_BIN_VER_OFFSET) + *(fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) { ++ NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n"); ++ NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", *(fw_entry->data+FW_BIN_VER_OFFSET), *(fw_entry->data+FW_BIN_VER_BAR_OFFSET)); ++ ret = -ENOEXEC; ++ goto invalid; ++ } ++ ++ /* BIN Header Parser */ ++ ret = nvt_bin_header_parser(fw_entry->data, fw_entry->size); ++ if (ret) { ++ NVT_ERR("bin header parser failed\n"); ++ goto invalid; ++ } else { ++ break; ++ } ++ ++invalid: ++ update_firmware_release(); ++ if (!IS_ERR_OR_NULL(bin_map)) { ++ kfree(bin_map); ++ bin_map = NULL; ++ } ++ ++request_fail: ++ retry++; ++ if(unlikely(retry > 2)) { ++ NVT_ERR("error, retry=%d\n", retry); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen write data to sram function. ++ ++- fwdata : The buffer is written ++- SRAM_addr: The sram destination address ++- size : Number of data bytes in @fwdata being written ++- BIN_addr : The transferred data offset of @fwdata ++ ++return: ++ Executive outcomes. 0---succeed. else---fail. ++*******************************************************/ ++static int32_t nvt_write_sram(const u8 *fwdata, ++ uint32_t SRAM_addr, uint32_t size, uint32_t BIN_addr) ++{ ++ int32_t ret = 0; ++ uint32_t i = 0; ++ uint16_t len = 0; ++ int32_t count = 0; ++ ++ if (size % NVT_TRANSFER_LEN) ++ count = (size / NVT_TRANSFER_LEN) + 1; ++ else ++ count = (size / NVT_TRANSFER_LEN); ++ ++ for (i = 0 ; i < count ; i++) { ++ len = (size < NVT_TRANSFER_LEN) ? size : NVT_TRANSFER_LEN; ++ ++ //---set xdata index to start address of SRAM--- ++ ret = nvt_set_page(SRAM_addr); ++ if (ret) { ++ NVT_ERR("set page failed, ret = %d\n", ret); ++ return ret; ++ } ++ ++ //---write data into SRAM--- ++ fwbuf[0] = SRAM_addr & 0x7F; //offset ++ memcpy(fwbuf+1, &fwdata[BIN_addr], len); //payload ++ ret = CTP_SPI_WRITE(ts->client, fwbuf, len+1); ++ if (ret) { ++ NVT_ERR("write to sram failed, ret = %d\n", ret); ++ return ret; ++ } ++ ++ SRAM_addr += NVT_TRANSFER_LEN; ++ BIN_addr += NVT_TRANSFER_LEN; ++ size -= NVT_TRANSFER_LEN; ++ } ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen nvt_write_firmware function to write ++firmware into each partition. ++ ++return: ++ n.a. ++*******************************************************/ ++static int32_t nvt_write_firmware(const u8 *fwdata, size_t fwsize) ++{ ++ uint32_t list = 0; ++ char *name; ++ uint32_t BIN_addr, SRAM_addr, size; ++ int32_t ret = 0; ++ ++ memset(fwbuf, 0, (NVT_TRANSFER_LEN+1)); ++ ++ for (list = 0; list < partition; list++) { ++ /* initialize variable */ ++ SRAM_addr = bin_map[list].SRAM_addr; ++ size = bin_map[list].size; ++ BIN_addr = bin_map[list].BIN_addr; ++ name = bin_map[list].name; ++ ++// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X)\n", ++// list, name, SRAM_addr, size, BIN_addr); ++ ++ /* Check data size */ ++ if ((BIN_addr + size) > fwsize) { ++ NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", ++ BIN_addr, BIN_addr + size); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* ignore reserved partition (Reserved Partition size is zero) */ ++ if (!size) ++ continue; ++ else ++ size = size +1; ++ ++ /* write data to SRAM */ ++ ret = nvt_write_sram(fwdata, SRAM_addr, size, BIN_addr); ++ if (ret) { ++ NVT_ERR("sram program failed, ret = %d\n", ret); ++ goto out; ++ } ++ } ++ ++out: ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen check checksum function. ++This function will compare file checksum and fw checksum. ++ ++return: ++ n.a. ++*******************************************************/ ++static int32_t nvt_check_fw_checksum(void) ++{ ++ uint32_t fw_checksum = 0; ++ uint32_t len = partition*4; ++ uint32_t list = 0; ++ int32_t ret = 0; ++ ++ memset(fwbuf, 0, (len+1)); ++ ++ //---set xdata index to checksum--- ++ nvt_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); ++ ++ /* read checksum */ ++ fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F; ++ ret = CTP_SPI_READ(ts->client, fwbuf, len+1); ++ if (ret) { ++ NVT_ERR("Read fw checksum failed\n"); ++ return ret; ++ } ++ ++ /* ++ * Compare each checksum from fw ++ * ILM + DLM + Overlay + Info ++ * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num ++ */ ++ for (list = 0; list < partition; list++) { ++ fw_checksum = byte_to_word(&fwbuf[1+list*4]); ++ ++ /* ignore reserved partition (Reserved Partition size is zero) */ ++ if(!bin_map[list].size) ++ continue; ++ ++ if (bin_map[list].crc != fw_checksum) { ++ NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n", ++ list, bin_map[list].crc, fw_checksum); ++ ret = -EIO; ++ } ++ } ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen set bootload crc reg bank function. ++This function will set hw crc reg before enable crc function. ++ ++return: ++ n.a. ++*******************************************************/ ++static void nvt_set_bld_crc_bank(uint32_t DES_ADDR, uint32_t SRAM_ADDR, ++ uint32_t LENGTH_ADDR, uint32_t size, ++ uint32_t G_CHECKSUM_ADDR, uint32_t crc) ++{ ++ /* write destination address */ ++ nvt_set_page(DES_ADDR); ++ fwbuf[0] = DES_ADDR & 0x7F; ++ fwbuf[1] = (SRAM_ADDR) & 0xFF; ++ fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF; ++ fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF; ++ CTP_SPI_WRITE(ts->client, fwbuf, 4); ++ ++ /* write length */ ++ //nvt_set_page(LENGTH_ADDR); ++ fwbuf[0] = LENGTH_ADDR & 0x7F; ++ fwbuf[1] = (size) & 0xFF; ++ fwbuf[2] = (size >> 8) & 0xFF; ++ fwbuf[3] = (size >> 16) & 0x01; ++ if (ts->hw_crc == 1) { ++ CTP_SPI_WRITE(ts->client, fwbuf, 3); ++ } else if (ts->hw_crc > 1) { ++ CTP_SPI_WRITE(ts->client, fwbuf, 4); ++ } ++ ++ /* write golden dlm checksum */ ++ //nvt_set_page(G_CHECKSUM_ADDR); ++ fwbuf[0] = G_CHECKSUM_ADDR & 0x7F; ++ fwbuf[1] = (crc) & 0xFF; ++ fwbuf[2] = (crc >> 8) & 0xFF; ++ fwbuf[3] = (crc >> 16) & 0xFF; ++ fwbuf[4] = (crc >> 24) & 0xFF; ++ CTP_SPI_WRITE(ts->client, fwbuf, 5); ++ ++ return; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen set BLD hw crc function. ++This function will set ILM and DLM crc information to register. ++ ++return: ++ n.a. ++*******************************************************/ ++static void nvt_set_bld_hw_crc(void) ++{ ++ /* [0] ILM */ ++ /* write register bank */ ++ nvt_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, bin_map[0].SRAM_addr, ++ ts->mmap->ILM_LENGTH_ADDR, bin_map[0].size, ++ ts->mmap->G_ILM_CHECKSUM_ADDR, bin_map[0].crc); ++ ++ /* [1] DLM */ ++ /* write register bank */ ++ nvt_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, bin_map[1].SRAM_addr, ++ ts->mmap->DLM_LENGTH_ADDR, bin_map[1].size, ++ ts->mmap->G_DLM_CHECKSUM_ADDR, bin_map[1].crc); ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen read BLD hw crc info function. ++This function will check crc results from register. ++ ++return: ++ n.a. ++*******************************************************/ ++static void nvt_read_bld_hw_crc(void) ++{ ++ uint8_t buf[8] = {0}; ++ uint32_t g_crc = 0, r_crc = 0; ++ ++ /* CRC Flag */ ++ nvt_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR); ++ buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F; ++ buf[1] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 2); ++ NVT_ERR("crc_done = %d, ilm_crc_flag = %d, dlm_crc_flag = %d\n", ++ (buf[1] >> 2) & 0x01, (buf[1] >> 0) & 0x01, (buf[1] >> 1) & 0x01); ++ ++ /* ILM CRC */ ++ nvt_set_page(ts->mmap->G_ILM_CHECKSUM_ADDR); ++ buf[0] = ts->mmap->G_ILM_CHECKSUM_ADDR & 0x7F; ++ buf[1] = 0x00; ++ buf[2] = 0x00; ++ buf[3] = 0x00; ++ buf[4] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 5); ++ g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); ++ ++ nvt_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); ++ buf[0] = ts->mmap->R_ILM_CHECKSUM_ADDR & 0x7F; ++ buf[1] = 0x00; ++ buf[2] = 0x00; ++ buf[3] = 0x00; ++ buf[4] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 5); ++ r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); ++ ++ NVT_ERR("ilm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n", ++ bin_map[0].crc, g_crc, r_crc); ++ ++ /* DLM CRC */ ++ nvt_set_page(ts->mmap->G_DLM_CHECKSUM_ADDR); ++ buf[0] = ts->mmap->G_DLM_CHECKSUM_ADDR & 0x7F; ++ buf[1] = 0x00; ++ buf[2] = 0x00; ++ buf[3] = 0x00; ++ buf[4] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 5); ++ g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); ++ ++ nvt_set_page(ts->mmap->R_DLM_CHECKSUM_ADDR); ++ buf[0] = ts->mmap->R_DLM_CHECKSUM_ADDR & 0x7F; ++ buf[1] = 0x00; ++ buf[2] = 0x00; ++ buf[3] = 0x00; ++ buf[4] = 0x00; ++ CTP_SPI_READ(ts->client, buf, 5); ++ r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); ++ ++ NVT_ERR("dlm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n", ++ bin_map[1].crc, g_crc, r_crc); ++ ++ return; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen Download_Firmware with HW CRC ++function. It's complete download firmware flow. ++ ++return: ++ Executive outcomes. 0---succeed. else---fail. ++*******************************************************/ ++static int32_t nvt_download_firmware_hw_crc(void) ++{ ++ uint8_t retry = 0; ++ int32_t ret = 0; ++ ++ start = ktime_get(); ++ ++ while (1) { ++ /* bootloader reset to reset MCU */ ++ nvt_bootloader_reset(); ++ ++ /* set ilm & dlm reg bank */ ++ nvt_set_bld_hw_crc(); ++ ++ /* Start to write firmware process */ ++ if (cascade_2nd_header_info) { ++ /* for cascade */ ++ nvt_tx_auto_copy_mode(); ++ ++ ret = nvt_write_firmware(fw_entry->data, fw_entry->size); ++ if (ret) { ++ NVT_ERR("Write_Firmware failed. (%d)\n", ret); ++ goto fail; ++ } ++ ++ ret = nvt_check_spi_dma_tx_info(); ++ if (ret) { ++ NVT_ERR("spi dma tx info failed. (%d)\n", ret); ++ goto fail; ++ } ++ } else { ++ ret = nvt_write_firmware(fw_entry->data, fw_entry->size); ++ if (ret) { ++ NVT_ERR("Write_Firmware failed. (%d)\n", ret); ++ goto fail; ++ } ++ } ++ ++ /* enable hw bld crc function */ ++ nvt_bld_crc_enable(); ++ ++ /* clear fw reset status & enable fw crc check */ ++ nvt_fw_crc_enable(); ++ ++ /* Set Boot Ready Bit */ ++ nvt_boot_ready(); ++ ++ ret = nvt_check_fw_reset_state(RESET_STATE_INIT); ++ if (ret) { ++ NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); ++ goto fail; ++ } else { ++ break; ++ } ++ ++fail: ++ retry++; ++ if(unlikely(retry > 2)) { ++ NVT_ERR("error, retry=%d\n", retry); ++ nvt_read_bld_hw_crc(); ++ break; ++ } ++ } ++ ++ end = ktime_get(); ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen Download_Firmware function. It's ++complete download firmware flow. ++ ++return: ++ n.a. ++*******************************************************/ ++static int32_t nvt_download_firmware(void) ++{ ++ uint8_t retry = 0; ++ int32_t ret = 0; ++ ++ start = ktime_get(); ++ ++ while (1) { ++ /* ++ * Send eng reset cmd before download FW ++ * Keep TP_RESX low when send eng reset cmd ++ */ ++#if NVT_TOUCH_SUPPORT_HW_RST ++ gpio_set_value(ts->reset_gpio, 0); ++ mdelay(1); //wait 1ms ++#endif ++ nvt_eng_reset(); ++#if NVT_TOUCH_SUPPORT_HW_RST ++ gpio_set_value(ts->reset_gpio, 1); ++ mdelay(10); //wait tRT2BRST after TP_RST ++#endif ++ nvt_bootloader_reset(); ++ ++ /* clear fw reset status */ ++ nvt_write_addr(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE, 0x00); ++ ++ /* Start to write firmware process */ ++ ret = nvt_write_firmware(fw_entry->data, fw_entry->size); ++ if (ret) { ++ NVT_ERR("Write_Firmware failed. (%d)\n", ret); ++ goto fail; ++ } ++ ++ /* Set Boot Ready Bit */ ++ nvt_boot_ready(); ++ ++ ret = nvt_check_fw_reset_state(RESET_STATE_INIT); ++ if (ret) { ++ NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); ++ goto fail; ++ } ++ ++ /* check fw checksum result */ ++ ret = nvt_check_fw_checksum(); ++ if (ret) { ++ NVT_ERR("firmware checksum not match, retry=%d\n", retry); ++ goto fail; ++ } else { ++ break; ++ } ++ ++fail: ++ retry++; ++ if(unlikely(retry > 2)) { ++ NVT_ERR("error, retry=%d\n", retry); ++ break; ++ } ++ } ++ ++ end = ktime_get(); ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen update firmware main function. ++ ++return: ++ n.a. ++*******************************************************/ ++int32_t nvt_update_firmware(const char *firmware_name) ++{ ++ int32_t ret = 0; ++ ++ // request bin file in "/etc/firmware" ++ ret = update_firmware_request(firmware_name); ++ if (ret) { ++ NVT_ERR("update_firmware_request failed. (%d)\n", ret); ++ goto request_firmware_fail; ++ } ++ ++ /* initial buffer and variable */ ++ ret = nvt_download_init(); ++ if (ret) { ++ NVT_ERR("Download Init failed. (%d)\n", ret); ++ goto download_fail; ++ } ++ ++ /* download firmware process */ ++ if (ts->hw_crc) ++ ret = nvt_download_firmware_hw_crc(); ++ else ++ ret = nvt_download_firmware(); ++ if (ret) { ++ NVT_ERR("Download Firmware failed. (%d)\n", ret); ++ goto download_fail; ++ } ++ ++ NVT_LOG("Update firmware success! <%ld us>\n", ++ (long) ktime_us_delta(end, start)); ++ ++ /* Get FW Info */ ++ ret = nvt_get_fw_info(); ++ if (ret) { ++ NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret); ++ } ++ ++download_fail: ++ if (!IS_ERR_OR_NULL(bin_map)) { ++ kfree(bin_map); ++ bin_map = NULL; ++ } ++ ++ update_firmware_release(); ++request_firmware_fail: ++ ++ return ret; ++} ++ ++/******************************************************* ++Description: ++ Novatek touchscreen update firmware when booting ++ function. ++ ++return: ++ n.a. ++*******************************************************/ ++void Boot_Update_Firmware(struct work_struct *work) ++{ ++ mutex_lock(&ts->lock); ++ nvt_update_firmware(ts->fw_name); ++ nvt_get_fw_info(); ++ mutex_unlock(&ts->lock); ++} ++#endif /* BOOT_UPDATE_FIRMWARE */ +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx_mem_map.h b/drivers/input/touchscreen/nt36523/nt36xxx_mem_map.h +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/nt36523/nt36xxx_mem_map.h +@@ -0,0 +1,390 @@ ++/* ++ * Copyright (C) 2010 - 2018 Novatek, Inc. ++ * Copyright (C) 2021 XiaoMi, Inc. ++ * ++ * $Revision: 57674 $ ++ * $Date: 2020-03-02 11:16:20 +0800 (週一, 02 三月 2020) $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#define CHIP_VER_TRIM_ADDR 0x3F004 ++#define CHIP_VER_TRIM_OLD_ADDR 0x1F64E ++ ++struct nvt_ts_mem_map { ++ uint32_t EVENT_BUF_ADDR; ++ uint32_t RAW_PIPE0_ADDR; ++ uint32_t RAW_PIPE1_ADDR; ++ uint32_t BASELINE_ADDR; ++ uint32_t BASELINE_BTN_ADDR; ++ uint32_t DIFF_PIPE0_ADDR; ++ uint32_t DIFF_PIPE1_ADDR; ++ uint32_t RAW_BTN_PIPE0_ADDR; ++ uint32_t RAW_BTN_PIPE1_ADDR; ++ uint32_t DIFF_BTN_PIPE0_ADDR; ++ uint32_t DIFF_BTN_PIPE1_ADDR; ++ uint32_t PEN_2D_BL_TIP_X_ADDR; ++ uint32_t PEN_2D_BL_TIP_Y_ADDR; ++ uint32_t PEN_2D_BL_RING_X_ADDR; ++ uint32_t PEN_2D_BL_RING_Y_ADDR; ++ uint32_t PEN_2D_DIFF_TIP_X_ADDR; ++ uint32_t PEN_2D_DIFF_TIP_Y_ADDR; ++ uint32_t PEN_2D_DIFF_RING_X_ADDR; ++ uint32_t PEN_2D_DIFF_RING_Y_ADDR; ++ uint32_t PEN_2D_RAW_TIP_X_ADDR; ++ uint32_t PEN_2D_RAW_TIP_Y_ADDR; ++ uint32_t PEN_2D_RAW_RING_X_ADDR; ++ uint32_t PEN_2D_RAW_RING_Y_ADDR; ++ uint32_t PEN_1D_DIFF_TIP_X_ADDR; ++ uint32_t PEN_1D_DIFF_TIP_Y_ADDR; ++ uint32_t PEN_1D_DIFF_RING_X_ADDR; ++ uint32_t PEN_1D_DIFF_RING_Y_ADDR; ++ uint32_t READ_FLASH_CHECKSUM_ADDR; ++ uint32_t RW_FLASH_DATA_ADDR; ++ /* Phase 2 Host Download */ ++ uint32_t BOOT_RDY_ADDR; ++ uint32_t POR_CD_ADDR; ++ uint32_t TX_AUTO_COPY_EN; ++ uint32_t SPI_DMA_TX_INFO; ++ /* BLD CRC */ ++ uint32_t BLD_LENGTH_ADDR; ++ uint32_t ILM_LENGTH_ADDR; ++ uint32_t DLM_LENGTH_ADDR; ++ uint32_t BLD_DES_ADDR; ++ uint32_t ILM_DES_ADDR; ++ uint32_t DLM_DES_ADDR; ++ uint32_t G_ILM_CHECKSUM_ADDR; ++ uint32_t G_DLM_CHECKSUM_ADDR; ++ uint32_t R_ILM_CHECKSUM_ADDR; ++ uint32_t R_DLM_CHECKSUM_ADDR; ++ uint32_t BLD_CRC_EN_ADDR; ++ uint32_t DMA_CRC_EN_ADDR; ++ uint32_t BLD_ILM_DLM_CRC_ADDR; ++ uint32_t DMA_CRC_FLAG_ADDR; ++ uint32_t FW_HISTORY_ADDR; ++}; ++ ++struct nvt_ts_hw_info { ++ uint8_t carrier_system; ++ uint8_t hw_crc; ++}; ++ ++static const struct nvt_ts_mem_map NT36523_memory_map = { ++ .EVENT_BUF_ADDR = 0x2FE00, ++ .RAW_PIPE0_ADDR = 0x30FA0, ++ .RAW_PIPE1_ADDR = 0x30FA0, ++ .BASELINE_ADDR = 0x36510, ++ .BASELINE_BTN_ADDR = 0, ++ .DIFF_PIPE0_ADDR = 0x373E8, ++ .DIFF_PIPE1_ADDR = 0x38068, ++ .RAW_BTN_PIPE0_ADDR = 0, ++ .RAW_BTN_PIPE1_ADDR = 0, ++ .DIFF_BTN_PIPE0_ADDR = 0, ++ .DIFF_BTN_PIPE1_ADDR = 0, ++ .PEN_2D_BL_TIP_X_ADDR = 0x2988A, ++ .PEN_2D_BL_TIP_Y_ADDR = 0x29A1A, ++ .PEN_2D_BL_RING_X_ADDR = 0x29BAA, ++ .PEN_2D_BL_RING_Y_ADDR = 0x29D3A, ++ .PEN_2D_DIFF_TIP_X_ADDR = 0x29ECA, ++ .PEN_2D_DIFF_TIP_Y_ADDR = 0x2A05A, ++ .PEN_2D_DIFF_RING_X_ADDR = 0x2A1EA, ++ .PEN_2D_DIFF_RING_Y_ADDR = 0x2A37A, ++ .PEN_2D_RAW_TIP_X_ADDR = 0x2A50A, ++ .PEN_2D_RAW_TIP_Y_ADDR = 0x2A69A, ++ .PEN_2D_RAW_RING_X_ADDR = 0x2A82A, ++ .PEN_2D_RAW_RING_Y_ADDR = 0x2A9BA, ++ .PEN_1D_DIFF_TIP_X_ADDR = 0x2AB4A, ++ .PEN_1D_DIFF_TIP_Y_ADDR = 0x2ABAE, ++ .PEN_1D_DIFF_RING_X_ADDR = 0x2AC12, ++ .PEN_1D_DIFF_RING_Y_ADDR = 0x2AC76, ++ .READ_FLASH_CHECKSUM_ADDR = 0x24000, ++ .RW_FLASH_DATA_ADDR = 0x24002, ++ /* Phase 2 Host Download */ ++ .BOOT_RDY_ADDR = 0x3F10D, ++ .TX_AUTO_COPY_EN = 0x3F7E8, ++ .SPI_DMA_TX_INFO = 0x3F7F1, ++ /* BLD CRC */ ++ .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) ++ .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) ++ .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) ++ .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) ++ .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) ++ .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) ++ .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) ++ .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) ++ .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) ++ .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) ++ .BLD_CRC_EN_ADDR = 0x3F30E, ++ .DMA_CRC_EN_ADDR = 0x3F136, ++ .BLD_ILM_DLM_CRC_ADDR = 0x3F133, ++ .DMA_CRC_FLAG_ADDR = 0x3F134, ++ .FW_HISTORY_ADDR = 0x38D54, ++}; ++ ++static const struct nvt_ts_mem_map NT36526_memory_map = { ++ .EVENT_BUF_ADDR = 0x22D00, ++ .RAW_PIPE0_ADDR = 0x24000, ++ .RAW_PIPE1_ADDR = 0x24000, ++ .BASELINE_ADDR = 0x21758, ++ .BASELINE_BTN_ADDR = 0, ++ .DIFF_PIPE0_ADDR = 0x20AB0, ++ .DIFF_PIPE1_ADDR = 0x24AB0, ++ .RAW_BTN_PIPE0_ADDR = 0, ++ .RAW_BTN_PIPE1_ADDR = 0, ++ .DIFF_BTN_PIPE0_ADDR = 0, ++ .DIFF_BTN_PIPE1_ADDR = 0, ++ .READ_FLASH_CHECKSUM_ADDR = 0x24000, ++ .RW_FLASH_DATA_ADDR = 0x24002, ++ /* Phase 2 Host Download */ ++ .BOOT_RDY_ADDR = 0x3F10D, ++ /* BLD CRC */ ++ .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) ++ .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) ++ .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) ++ .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) ++ .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) ++ .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) ++ .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) ++ .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) ++ .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) ++ .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) ++ .BLD_CRC_EN_ADDR = 0x3F30E, ++ .DMA_CRC_EN_ADDR = 0x3F136, ++ .BLD_ILM_DLM_CRC_ADDR = 0x3F133, ++ .DMA_CRC_FLAG_ADDR = 0x3F134, ++}; ++ ++ ++static const struct nvt_ts_mem_map NT36675_memory_map = { ++ .EVENT_BUF_ADDR = 0x22D00, ++ .RAW_PIPE0_ADDR = 0x24000, ++ .RAW_PIPE1_ADDR = 0x24000, ++ .BASELINE_ADDR = 0x21B90, ++ .BASELINE_BTN_ADDR = 0, ++ .DIFF_PIPE0_ADDR = 0x20C60, ++ .DIFF_PIPE1_ADDR = 0x24C60, ++ .RAW_BTN_PIPE0_ADDR = 0, ++ .RAW_BTN_PIPE1_ADDR = 0, ++ .DIFF_BTN_PIPE0_ADDR = 0, ++ .DIFF_BTN_PIPE1_ADDR = 0, ++ .READ_FLASH_CHECKSUM_ADDR = 0x24000, ++ .RW_FLASH_DATA_ADDR = 0x24002, ++ /* Phase 2 Host Download */ ++ .BOOT_RDY_ADDR = 0x3F10D, ++ /* BLD CRC */ ++ .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) ++ .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) ++ .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) ++ .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) ++ .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) ++ .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) ++ .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) ++ .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) ++ .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) ++ .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) ++ .BLD_CRC_EN_ADDR = 0x3F30E, ++ .DMA_CRC_EN_ADDR = 0x3F136, ++ .BLD_ILM_DLM_CRC_ADDR = 0x3F133, ++ .DMA_CRC_FLAG_ADDR = 0x3F134, ++}; ++ ++ ++static const struct nvt_ts_mem_map NT36672A_memory_map = { ++ .EVENT_BUF_ADDR = 0x21C00, ++ .RAW_PIPE0_ADDR = 0x20000, ++ .RAW_PIPE1_ADDR = 0x23000, ++ .BASELINE_ADDR = 0x20BFC, ++ .BASELINE_BTN_ADDR = 0x23BFC, ++ .DIFF_PIPE0_ADDR = 0x206DC, ++ .DIFF_PIPE1_ADDR = 0x236DC, ++ .RAW_BTN_PIPE0_ADDR = 0x20510, ++ .RAW_BTN_PIPE1_ADDR = 0x23510, ++ .DIFF_BTN_PIPE0_ADDR = 0x20BF0, ++ .DIFF_BTN_PIPE1_ADDR = 0x23BF0, ++ .READ_FLASH_CHECKSUM_ADDR = 0x24000, ++ .RW_FLASH_DATA_ADDR = 0x24002, ++ /* Phase 2 Host Download */ ++ .BOOT_RDY_ADDR = 0x3F10D, ++ /* BLD CRC */ ++ .BLD_LENGTH_ADDR = 0x3F10E, //0x3F10E ~ 0x3F10F (2 bytes) ++ .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F119 (2 bytes) ++ .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F131 (2 bytes) ++ .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) ++ .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) ++ .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) ++ .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) ++ .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) ++ .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) ++ .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) ++ .BLD_CRC_EN_ADDR = 0x3F30E, ++ .DMA_CRC_EN_ADDR = 0x3F132, ++ .BLD_ILM_DLM_CRC_ADDR = 0x3F133, ++ .DMA_CRC_FLAG_ADDR = 0x3F134, ++}; ++ ++static const struct nvt_ts_mem_map NT36772_memory_map = { ++ .EVENT_BUF_ADDR = 0x11E00, ++ .RAW_PIPE0_ADDR = 0x10000, ++ .RAW_PIPE1_ADDR = 0x12000, ++ .BASELINE_ADDR = 0x10E70, ++ .BASELINE_BTN_ADDR = 0x12E70, ++ .DIFF_PIPE0_ADDR = 0x10830, ++ .DIFF_PIPE1_ADDR = 0x12830, ++ .RAW_BTN_PIPE0_ADDR = 0x10E60, ++ .RAW_BTN_PIPE1_ADDR = 0x12E60, ++ .DIFF_BTN_PIPE0_ADDR = 0x10E68, ++ .DIFF_BTN_PIPE1_ADDR = 0x12E68, ++ .READ_FLASH_CHECKSUM_ADDR = 0x14000, ++ .RW_FLASH_DATA_ADDR = 0x14002, ++ /* Phase 2 Host Download */ ++ .BOOT_RDY_ADDR = 0x1F141, ++ .POR_CD_ADDR = 0x1F61C, ++ /* BLD CRC */ ++ .R_ILM_CHECKSUM_ADDR = 0x1BF00, ++}; ++ ++static const struct nvt_ts_mem_map NT36525_memory_map = { ++ .EVENT_BUF_ADDR = 0x11A00, ++ .RAW_PIPE0_ADDR = 0x10000, ++ .RAW_PIPE1_ADDR = 0x12000, ++ .BASELINE_ADDR = 0x10B08, ++ .BASELINE_BTN_ADDR = 0x12B08, ++ .DIFF_PIPE0_ADDR = 0x1064C, ++ .DIFF_PIPE1_ADDR = 0x1264C, ++ .RAW_BTN_PIPE0_ADDR = 0x10634, ++ .RAW_BTN_PIPE1_ADDR = 0x12634, ++ .DIFF_BTN_PIPE0_ADDR = 0x10AFC, ++ .DIFF_BTN_PIPE1_ADDR = 0x12AFC, ++ .READ_FLASH_CHECKSUM_ADDR = 0x14000, ++ .RW_FLASH_DATA_ADDR = 0x14002, ++ /* Phase 2 Host Download */ ++ .BOOT_RDY_ADDR = 0x1F141, ++ .POR_CD_ADDR = 0x1F61C, ++ /* BLD CRC */ ++ .R_ILM_CHECKSUM_ADDR = 0x1BF00, ++}; ++ ++static const struct nvt_ts_mem_map NT36676F_memory_map = { ++ .EVENT_BUF_ADDR = 0x11A00, ++ .RAW_PIPE0_ADDR = 0x10000, ++ .RAW_PIPE1_ADDR = 0x12000, ++ .BASELINE_ADDR = 0x10B08, ++ .BASELINE_BTN_ADDR = 0x12B08, ++ .DIFF_PIPE0_ADDR = 0x1064C, ++ .DIFF_PIPE1_ADDR = 0x1264C, ++ .RAW_BTN_PIPE0_ADDR = 0x10634, ++ .RAW_BTN_PIPE1_ADDR = 0x12634, ++ .DIFF_BTN_PIPE0_ADDR = 0x10AFC, ++ .DIFF_BTN_PIPE1_ADDR = 0x12AFC, ++ .READ_FLASH_CHECKSUM_ADDR = 0x14000, ++ .RW_FLASH_DATA_ADDR = 0x14002, ++}; ++ ++static struct nvt_ts_hw_info NT36523_hw_info = { ++ .carrier_system = 2, ++ .hw_crc = 2, ++}; ++ ++static struct nvt_ts_hw_info NT36526_hw_info = { ++ .carrier_system = 2, ++ .hw_crc = 2, ++}; ++ ++static struct nvt_ts_hw_info NT36675_hw_info = { ++ .carrier_system = 2, ++ .hw_crc = 2, ++}; ++ ++static struct nvt_ts_hw_info NT36672A_hw_info = { ++ .carrier_system = 0, ++ .hw_crc = 1, ++}; ++ ++static struct nvt_ts_hw_info NT36772_hw_info = { ++ .carrier_system = 0, ++ .hw_crc = 0, ++}; ++ ++static struct nvt_ts_hw_info NT36525_hw_info = { ++ .carrier_system = 0, ++ .hw_crc = 0, ++}; ++ ++static struct nvt_ts_hw_info NT36676F_hw_info = { ++ .carrier_system = 0, ++ .hw_crc = 0, ++}; ++ ++#define NVT_ID_BYTE_MAX 6 ++struct nvt_ts_trim_id_table { ++ uint8_t id[NVT_ID_BYTE_MAX]; ++ uint8_t mask[NVT_ID_BYTE_MAX]; ++ const struct nvt_ts_mem_map *mmap; ++ const struct nvt_ts_hw_info *hwinfo; ++}; ++ ++static const struct nvt_ts_trim_id_table trim_id_table[] = { ++ {.id = {0x20, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, ++ {.id = {0x0C, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, ++ {.id = {0x0B, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, ++ {.id = {0x0A, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, ++ {.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, ++ {.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, ++ .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, ++ {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info}, ++ {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, ++ .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info} ++}; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0005-arm64-dts-qcom-sm8250-xiaomi-elish-enable-touchscree.patch b/patch/kernel/archive/sm8250-6.18/0005-arm64-dts-qcom-sm8250-xiaomi-elish-enable-touchscree.patch new file mode 100644 index 000000000000..516e29c66a0e --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0005-arm64-dts-qcom-sm8250-xiaomi-elish-enable-touchscree.patch @@ -0,0 +1,91 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Mon, 6 Mar 2023 23:23:13 +0800 +Subject: arm64: dts: qcom: sm8250-xiaomi-elish: enable touchscreen + +Signed-off-by: Jianhua Lu +--- + arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-boe.dts | 5 ++ + arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi | 32 ++++++++++ + arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-csot.dts | 5 ++ + 3 files changed, 42 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-boe.dts b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-boe.dts +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-boe.dts ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-boe.dts +@@ -16,3 +16,8 @@ &display_panel { + compatible = "xiaomi,elish-boe-nt36523", "novatek,nt36523"; + status = "okay"; + }; ++ ++&touchscreen { ++ firmware-name = "novatek/nt36523-boe.bin"; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +@@ -766,6 +766,16 @@ &pon_resin { + status = "okay"; + }; + ++&qup_spi4_cs_gpio { ++ drive-strength = <6>; ++ bias-disable; ++}; ++ ++&qup_spi4_data_clk { ++ drive-strength = <6>; ++ bias-disable; ++}; ++ + &qupv3_id_0 { + status = "okay"; + }; +@@ -783,6 +793,28 @@ &slpi { + status = "okay"; + }; + ++&spi4 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&qup_spi4_data_clk &qup_spi4_cs_gpio>; ++ cs-gpios = <&tlmm 11 GPIO_ACTIVE_LOW>; ++ touchscreen: touchscreen@0 { ++ compatible = "novatek,NVT-ts-spi"; ++ reg = <0>; //Same as CS ID ++ status = "disabled"; ++ ++ spi-max-frequency = <19200000>; //4800000,9600000,15000000,19200000 ++ novatek,irq-gpio = <&tlmm 39 0x2001>; ++ ++ novatek,pen-support; ++ novatek,wgp-stylus; ++ ++ /* 523 */ ++ novatek,swrst-n8-addr = <0x03F0FE>; ++ novatek,spi-rd-fast-addr = <0x03F310>; ++ }; ++}; ++ + &tlmm { + gpio-reserved-ranges = <40 4>; + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-csot.dts b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-csot.dts +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-csot.dts ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-csot.dts +@@ -16,3 +16,8 @@ &display_panel { + compatible = "xiaomi,elish-csot-nt36523", "novatek,nt36523"; + status = "okay"; + }; ++ ++&touchscreen { ++ firmware-name = "novatek/nt36523-csot.bin"; ++ status = "okay"; ++}; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0006-ASoC-qcom-sm8250-Add-tdm-support.patch b/patch/kernel/archive/sm8250-6.18/0006-ASoC-qcom-sm8250-Add-tdm-support.patch new file mode 100644 index 000000000000..a1a63d040936 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0006-ASoC-qcom-sm8250-Add-tdm-support.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Mon, 11 Dec 2023 09:23:28 +0800 +Subject: ASoC: qcom: sm8250: Add tdm support + +--- + sound/soc/qcom/sm8250.c | 75 ++++++++++ + 1 file changed, 75 insertions(+) + +diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c +index 111111111111..222222222222 100644 +--- a/sound/soc/qcom/sm8250.c ++++ b/sound/soc/qcom/sm8250.c +@@ -16,6 +16,9 @@ + + #define DRIVER_NAME "sm8250" + #define MI2S_BCLK_RATE 1536000 ++#define TDM_BCLK_RATE 12288000 ++ ++static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + struct sm8250_snd_data { + bool stream_prepared[AFE_PORT_MAX]; +@@ -32,6 +35,57 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + } + ++static int sm8250_tdm_snd_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ ++ int ret = 0; ++ int channels, slots, slot_width; ++ ++ channels = params_channels(params); ++ slots = 8; ++ slot_width = 32; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x03, ++ slots, slot_width); ++ if (ret < 0) { ++ dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", ++ __func__, ret); ++ goto end; ++ } ++ ++ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, ++ channels, tdm_slot_offset); ++ if (ret < 0) { ++ dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", ++ __func__, ret); ++ goto end; ++ } ++ } else { ++ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0, ++ slots, slot_width); ++ if (ret < 0) { ++ dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", ++ __func__, ret); ++ goto end; ++ } ++ ++ ret = snd_soc_dai_set_channel_map(cpu_dai, channels, ++ tdm_slot_offset, 0, NULL); ++ if (ret < 0) { ++ dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", ++ __func__, ret); ++ goto end; ++ } ++ } ++ ++end: ++ return ret; ++} ++ + static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) + { +@@ -53,6 +107,7 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); ++ int ret,j; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: +@@ -71,6 +126,21 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; ++ case TERTIARY_TDM_RX_0: ++ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_DSP_A; ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT, ++ TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); ++ ++ for_each_rtd_codec_dais(rtd, j, codec_dai) { ++ ret = snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); ++ snd_soc_dai_set_sysclk(codec_dai, 0, TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); ++ if (ret < 0) { ++ dev_err(rtd->dev, "TDM fmt err:%d\n", ret); ++ return ret; ++ } ++ } ++ break; + default: + break; + } +@@ -96,6 +166,11 @@ static int sm8250_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sm8250_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); + ++ switch (cpu_dai->id) { ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ return sm8250_tdm_snd_hw_params(substream, params); ++ } ++ + return qcom_snd_sdw_hw_params(substream, params, &pdata->sruntime[cpu_dai->id]); + } + +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0007-arm64-dts-qcom-sm8250-xiaomi-elish-Add-sound-support.patch b/patch/kernel/archive/sm8250-6.18/0007-arm64-dts-qcom-sm8250-xiaomi-elish-Add-sound-support.patch new file mode 100644 index 000000000000..869ac8acf6f4 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0007-arm64-dts-qcom-sm8250-xiaomi-elish-Add-sound-support.patch @@ -0,0 +1,281 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Sat, 18 Mar 2023 22:26:22 +0800 +Subject: arm64: dts: qcom: sm8250-xiaomi-elish: Add sound support + +--- + arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi | 232 ++++++++++ + 1 file changed, 232 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +@@ -6,6 +6,8 @@ + #include + #include + #include ++#include ++#include + #include + #include "sm8250.dtsi" + #include "pm8150.dtsi" +@@ -572,6 +574,152 @@ fuel-gauge@55 { + }; + }; + ++&i2c1 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ cs35l41_brh: speaker-amp@40 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x40>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <7 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "BRH"; ++ #sound-dai-cells = <1>; ++ }; ++ ++ cs35l41_blh: speaker-amp@41 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x41>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <67 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 62 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "BLH"; ++ #sound-dai-cells = <1>; ++ }; ++ ++ cs35l41_brl: speaker-amp@42 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x42>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <100 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 69 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "BRL"; ++ #sound-dai-cells = <1>; ++ }; ++ ++ cs35l41_bll: speaker-amp@43 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x43>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <126 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 49 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "BLL"; ++ #sound-dai-cells = <1>; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ cs35l41_trh: speaker-amp@40 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x40>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <27 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "TRH"; ++ #sound-dai-cells = <1>; ++ }; ++ ++ cs35l41_tlh: speaker-amp@41 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x41>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <92 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 78 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "TLH"; ++ #sound-dai-cells = <1>; ++ }; ++ ++ cs35l41_tll: speaker-amp@42 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x42>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <112 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 30 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "TLL"; ++ #sound-dai-cells = <1>; ++ }; ++ ++ cs35l41_trl: speaker-amp@43 { ++ compatible = "cirrus,cs35l41"; ++ reg = <0x43>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <129 IRQ_TYPE_LEVEL_LOW>; ++ reset-gpios = <&tlmm 144 GPIO_ACTIVE_HIGH>; ++ cirrus,boost-type = <0>; ++ cirrus,boost-peak-milliamp = <4000>; ++ cirrus,boost-ind-nanohenry = <1000>; ++ cirrus,boost-cap-microfarad = <15>; ++ cirrus,asp-sdout-hiz = <3>; ++ cirrus,gpio2-src-select = <4>; ++ cirrus,gpio2-output-enable; ++ sound-name-prefix = "TRL"; ++ #sound-dai-cells = <1>; ++ }; ++}; ++ + &i2c11 { + clock-frequency = <400000>; + status = "okay"; +@@ -788,11 +936,63 @@ &qupv3_id_2 { + status = "okay"; + }; + ++&q6afedai { ++ dai@56 { ++ reg = ; ++ qcom,tdm-sync-mode = <0>; ++ qcom,tdm-sync-src = <1>; ++ qcom,tdm-data-out = <0>; ++ qcom,tdm-invert-sync = <1>; ++ qcom,tdm-data-delay = <1>; ++ qcom,tdm-data-align = <0>; ++ }; ++}; ++ ++&q6asmdai { ++ dai@0 { ++ reg = <0>; ++ }; ++}; ++ + &slpi { + firmware-name = "qcom/sm8250/xiaomi/elish/slpi.mbn"; + status = "okay"; + }; + ++&sound { ++ compatible = "qcom,sm8250-sndcard"; ++ model = "Xiaomi Mi Pad 5 Pro"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tert_tdm_active>; ++ ++ mm1-dai-link { ++ link-name = "MultiMedia1"; ++ ++ cpu { ++ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; ++ }; ++ }; ++ ++ speaker-dai-link { ++ link-name = "Tertiary TDM Playback"; ++ ++ cpu { ++ sound-dai = <&q6afedai TERTIARY_TDM_RX_0>; ++ }; ++ ++ platform { ++ sound-dai = <&q6routing>; ++ }; ++ ++ codec { ++ sound-dai = <&cs35l41_tlh 0>, <&cs35l41_tll 0>, ++ <&cs35l41_trh 0>, <&cs35l41_trl 0>, ++ <&cs35l41_blh 0>, <&cs35l41_bll 0>, ++ <&cs35l41_brh 0>, <&cs35l41_brl 0>; ++ }; ++ }; ++}; ++ + &spi4 { + status = "okay"; + pinctrl-names = "default"; +@@ -833,6 +1033,38 @@ wlan_en_state: wlan-default-state { + output-low; + bias-pull-up; + }; ++ ++ tert_tdm_active: tert-tdm-active-state { ++ sck-pins { ++ pins = "gpio133"; ++ function = "mi2s2_sck"; ++ drive-strength = <8>; ++ bias-disable; ++ output-high; ++ }; ++ ++ din-pins { ++ pins = "gpio134"; ++ function = "mi2s2_data0"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ ++ ws-pins { ++ pins = "gpio135"; ++ function = "mi2s2_ws"; ++ drive-strength = <8>; ++ bias-disable; ++ output-high; ++ }; ++ ++ dout-pins { ++ pins = "gpio137"; ++ function = "mi2s2_data1"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + &uart6 { +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0008-Asoc-wm_adsp-Add-prefix-support.patch b/patch/kernel/archive/sm8250-6.18/0008-Asoc-wm_adsp-Add-prefix-support.patch new file mode 100644 index 000000000000..0bab90d1bad5 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0008-Asoc-wm_adsp-Add-prefix-support.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Wed, 29 Mar 2023 19:38:33 +0800 +Subject: Asoc: wm_adsp: Add prefix support + +--- + sound/soc/codecs/wm_adsp.c | 14 ++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +index 111111111111..222222222222 100644 +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -751,6 +751,10 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part, + fwf, wm_adsp_fw[dsp->fw].file, system_name, + filetype); ++ else if (asoc_component_prefix) ++ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part, ++ fwf, wm_adsp_fw[dsp->fw].file, asoc_component_prefix, ++ filetype); + else + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf, + wm_adsp_fw[dsp->fw].file, filetype); +@@ -822,6 +826,16 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, + NULL, "bin"); + return 0; + } ++ } else if (suffix) { ++ if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, ++ cirrus_dir, NULL, ++ NULL, "wmfw")) { ++ adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename); ++ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, ++ cirrus_dir, NULL, ++ suffix, "bin"); ++ return 0; ++ } + } + + /* Check system-specific bin without wmfw before falling back to generic */ +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0009-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Add-dev.patch b/patch/kernel/archive/sm8250-6.18/0009-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Add-dev.patch new file mode 100644 index 000000000000..54435bcaea12 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0009-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Add-dev.patch @@ -0,0 +1,851 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Pangwalla +Date: Fri, 10 Mar 2023 19:09:42 -0500 +Subject: arm64: dts: qcom: sm8250-oneplus-instantnoodlep: Add device tree for + Oneplus 8 Pro + +--- + arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts | 832 ++++++++++ + 1 file changed, 832 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts b/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts +@@ -0,0 +1,832 @@ ++/dts-v1/; ++ ++#include ++#include ++#include ++#include ++#include "sm8250.dtsi" ++#include "pm8150.dtsi" ++#include "pm8150b.dtsi" ++#include "pm8150l.dtsi" ++#include "pm8009.dtsi" ++ ++/* ++ * Delete following upstream (sm8250.dtsi) reserved ++ * memory mappings which are different in this device. ++ */ ++/delete-node/ &removed_mem; ++/delete-node/ &camera_mem; ++/delete-node/ &wlan_mem; ++/delete-node/ &ipa_fw_mem; ++/delete-node/ &ipa_gsi_mem; ++/delete-node/ &gpu_mem; ++/delete-node/ &npu_mem; ++/delete-node/ &video_mem; ++/delete-node/ &cvp_mem; ++/delete-node/ &cdsp_mem; ++/delete-node/ &slpi_mem; ++/delete-node/ &adsp_mem; ++/delete-node/ &spss_mem; ++/delete-node/ &cdsp_secure_heap; ++ ++/ { ++ model = "OnePlus 8 Pro (instantnoodlep)"; ++ compatible = "oneplus,instantnoodlep", "qcom,kona", "qcom,sm8250"; ++ chassis-type = "handset"; ++ ++ qcom,msm-id = <0x164 0x10000>, <0x164 0x20001>; ++ qcom,board-id = <0x08 0x00>, <0x00 0x00>; ++ ++ aliases { ++ hsuart0 = &uart6; ++ }; ++ ++ chosen { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ stdout-path = "framebuffer0"; ++ ++ framebuffer0: framebuffer@9c000000 { ++ compatible = "simple-framebuffer"; ++ reg = <0x0 0x9c000000 0x0 (1080 * 2376 * 4)>; ++ width = <1080>; ++ height = <2376>; ++ stride = <(1080 * 4)>; ++ format = "a8r8g8b8"; ++ }; ++ }; ++ ++ gpio_keys: gpio-keys { ++ compatible = "gpio-keys"; ++ ++ vol-up { ++ label = "Volume Up"; ++ gpios = <&pm8150_gpios 6 GPIO_ACTIVE_LOW>; ++ linux,code = ; ++ debounce-interval = <15>; ++ linux,can-disable; ++ gpio-key,wakeup; ++ }; ++ ++ vol-down { ++ label = "Volume Down"; ++ linux,code = ; ++ gpios = <&pm8150_gpios 7 GPIO_ACTIVE_LOW>; ++ debounce-interval = <15>; ++ linux,can-disable; ++ gpio-key,wakeup; ++ }; ++ }; ++ ++ reserved-memory { ++ removed_mem: memory@80b00000 { ++ reg = <0x0 0x80b00000 0x0 0xcd00000>; ++ no-map; ++ }; ++ ++ camera_mem: memory@8dc00000 { ++ reg = <0x0 0x8dc00000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ wlan_mem: memory@8e100000 { ++ reg = <0x0 0x8e100000 0x0 0x100000>; ++ no-map; ++ }; ++ ++ ipa_fw_mem: memory@8e200000 { ++ reg = <0x0 0x8e200000 0x0 0x10000>; ++ no-map; ++ }; ++ ++ ipa_gsi_mem: memory@8e210000 { ++ reg = <0x0 0x8e210000 0x0 0xa000>; ++ no-map; ++ }; ++ ++ gpu_mem: memory@8e21a000 { ++ reg = <0x0 0x8e21a000 0x0 0x2000>; ++ no-map; ++ }; ++ ++ npu_mem: memory@8e300000 { ++ reg = <0x0 0x8e300000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ video_mem: memory@8e800000 { ++ reg = <0x0 0x8e800000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ cvp_mem: memory@8ed00000 { ++ reg = <0x0 0x8ed00000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ cdsp_mem: memory@8f200000 { ++ reg = <0x0 0x8f200000 0x0 0x1400000>; ++ no-map; ++ }; ++ ++ slpi_mem: memory@90600000 { ++ reg = <0x0 0x90600000 0x0 0x1500000>; ++ no-map; ++ }; ++ ++ adsp_mem: memory@91b00000 { ++ reg = <0x00 0x91b00000 0x00 0x2500000>; ++ no-map; ++ }; ++ ++ spss_mem: memory@94000000 { ++ reg = <0x0 0x94000000 0x0 0x100000>; ++ no-map; ++ }; ++ ++ cdsp_secure_heap: memory@94100000 { ++ reg = <0x0 0x94100000 0x0 0x4600000>; ++ no-map; ++ }; ++ ++ cont_splash_mem: memory@9c000000 { ++ reg = <0x0 0x9c000000 0x0 0x2300000>; ++ no-map; ++ }; ++ ++ ramoops@b0000000 { ++ compatible = "ramoops"; ++ reg = <0x00 0xb0000000 0x00 0x400000>; ++ record-size = <0x40000>; ++ console-size = <0x40000>; ++ ecc-size = <0x00>; ++ no-map; ++ }; ++ }; ++ ++ vph_pwr: vph-pwr-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vph_pwr"; ++ regulator-min-microvolt = <3700000>; ++ regulator-max-microvolt = <3700000>; ++ }; ++ ++ vreg_s6c_0p88: smpc6-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vreg_s6c_0p88"; ++ regulator-min-microvolt = <880000>; ++ regulator-max-microvolt = <880000>; ++ regulator-always-on; ++ vin-supply = <&vph_pwr>; ++ }; ++ ++ vreg_s4a_1p8: vreg-s4a-1p8 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vreg_s4a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ }; ++ ++ qca639x: qca639x { ++ compatible = "qcom,qca6390"; ++ #power-domain-cells = <0>; ++ ++ vddaon-supply = <&vreg_s6a_0p95>; ++ vddpmu-supply = <&vreg_s2f_0p95>; ++ vddrfa1-supply = <&vreg_s2f_0p95>; ++ vddrfa2-supply = <&vreg_s8c_1p3>; ++ vddrfa3-supply = <&vreg_s5a_1p9>; ++ vddpcie1-supply = <&vreg_s8c_1p3>; ++ vddpcie2-supply = <&vreg_s5a_1p9>; ++ vddio-supply = <&vreg_s4a_1p8>; ++ ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&wlan_en_active>; ++ pinctrl-1 = <&wlan_en_sleep>; ++ ++ wlan-en-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; ++ }; ++ ++ battery: battery { ++ compatible = "simple-battery"; ++ ++ charge-full-design-microamp-hours = <4410000>; ++ energy-full-design-microwatt-hours = <17060000>; ++ voltage-max-design-microvolt = <4350000>; ++ }; ++}; ++ ++&apps_rsc { ++ regulators-0 { ++ compatible = "qcom,pm8009-1-rpmh-regulators"; ++ qcom,pmic-id = "f"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-l2-supply = <&vreg_s8c_1p3>; ++ vdd-l5-l6-supply = <&vreg_bob>; ++ vdd-l7-supply = <&vreg_s4a_1p8>; ++ ++ vreg_s2f_0p95: smps2 { ++ regulator-name = "vreg_s2f_0p95"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <952000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l1f_1p1: ldo1 { ++ regulator-name = "vreg_l1f_1p1"; ++ regulator-min-microvolt = <1104000>; ++ regulator-max-microvolt = <1104000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l2f_1p2: ldo2 { ++ regulator-name = "vreg_l2f_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6f_2p8: ldo6 { ++ regulator-name = "vreg_l6f_2p8"; ++ regulator-min-microvolt = <2800000>; ++ regulator-max-microvolt = <2800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l7f_1p8: ldo7 { ++ regulator-name = "vreg_l7f_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ }; ++ ++ regulators-1 { ++ compatible = "qcom,pm8150-rpmh-regulators"; ++ qcom,pmic-id = "a"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-s3-supply = <&vph_pwr>; ++ vdd-s4-supply = <&vph_pwr>; ++ vdd-s5-supply = <&vph_pwr>; ++ vdd-s6-supply = <&vph_pwr>; ++ vdd-s7-supply = <&vph_pwr>; ++ vdd-s8-supply = <&vph_pwr>; ++ vdd-s9-supply = <&vph_pwr>; ++ vdd-s10-supply = <&vph_pwr>; ++ vdd-l2-l10-supply = <&vreg_bob>; ++ vdd-l3-l4-l5-l18-supply = <&vreg_s6a_0p95>; ++ vdd-l6-l9-supply = <&vreg_s8c_1p3>; ++ vdd-l7-l12-l14-l15-supply = <&vreg_s5a_1p9>; ++ vdd-l13-l16-l17-supply = <&vreg_bob>; ++ ++ vreg_l2a_3p1: ldo2 { ++ regulator-name = "vreg_l2a_3p1"; ++ regulator-min-microvolt = <3072000>; ++ regulator-max-microvolt = <3072000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l3a_0p9: ldo3 { ++ regulator-name = "vreg_l3a_0p9"; ++ regulator-min-microvolt = <928000>; ++ regulator-max-microvolt = <932000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l5a_0p88: ldo5 { ++ regulator-name = "vreg_l5a_0p88"; ++ regulator-min-microvolt = <880000>; ++ regulator-max-microvolt = <880000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6a_1p2: ldo6 { ++ regulator-name = "vreg_l6a_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l7a_1p7: ldo7 { ++ regulator-name = "vreg_l7a_1p7"; ++ regulator-min-microvolt = <1704000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l9a_1p2: ldo9 { ++ regulator-name = "vreg_l9a_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l10a_1p8: ldo10 { ++ regulator-name = "vreg_l10a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l12a_1p8: ldo12 { ++ regulator-name = "vreg_l12a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l13a_ts_3p0: ldo13 { ++ regulator-name = "vreg_l13a_ts_3p0"; ++ regulator-min-microvolt = <3008000>; ++ regulator-max-microvolt = <3008000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l14a_1p8: ldo14 { ++ regulator-name = "vreg_l14a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1880000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l15a_1p8: ldo15 { ++ regulator-name = "vreg_l15a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l16a_2p7: ldo16 { ++ regulator-name = "vreg_l16a_2p7"; ++ regulator-min-microvolt = <2704000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l17a_3p0: ldo17 { ++ regulator-name = "vreg_l17a_3p0"; ++ regulator-min-microvolt = <2856000>; ++ regulator-max-microvolt = <3008000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l18a_0p92: ldo18 { ++ regulator-name = "vreg_l18a_0p92"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <912000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_s5a_1p9: smps5 { ++ regulator-name = "vreg_s5a_1p9"; ++ regulator-min-microvolt = <1904000>; ++ regulator-max-microvolt = <2000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_s6a_0p95: smps6 { ++ regulator-name = "vreg_s6a_0p95"; ++ regulator-min-microvolt = <920000>; ++ regulator-max-microvolt = <1128000>; ++ regulator-initial-mode = ; ++ }; ++ }; ++ ++ regulators-2 { ++ compatible = "qcom,pm8150l-rpmh-regulators"; ++ qcom,pmic-id = "c"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-s3-supply = <&vph_pwr>; ++ vdd-s4-supply = <&vph_pwr>; ++ vdd-s5-supply = <&vph_pwr>; ++ vdd-s6-supply = <&vph_pwr>; ++ vdd-s7-supply = <&vph_pwr>; ++ vdd-s8-supply = <&vph_pwr>; ++ vdd-l1-l8-supply = <&vreg_s4a_1p8>; ++ vdd-l2-l3-supply = <&vreg_s8c_1p3>; ++ vdd-l4-l5-l6-supply = <&vreg_bob>; ++ vdd-l7-l11-supply = <&vreg_bob>; ++ vdd-l9-l10-supply = <&vreg_bob>; ++ vdd-bob-supply = <&vph_pwr>; ++ ++ vreg_bob: bob { ++ regulator-name = "vreg_bob"; ++ regulator-min-microvolt = <3008000>; ++ regulator-max-microvolt = <4000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l1c_1p8: ldo1 { ++ regulator-name = "vreg_l1c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l2c_1p2: ldo2 { ++ regulator-name = "vreg_l2c_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l3c_0p8: ldo3 { ++ regulator-name = "vreg_l3c_0p8"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l4c_1p7: ldo4 { ++ regulator-name = "vreg_l4c_1p7"; ++ regulator-min-microvolt = <1704000>; ++ regulator-max-microvolt = <2928000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l5c_1p8: ldo5 { ++ regulator-name = "vreg_l5c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2928000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6c_2p96: ldo6 { ++ regulator-name = "vreg_l6c_2p96"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l7c_cam_vcm0_2p85: ldo7 { ++ regulator-name = "vreg_l7c_cam_vcm0_2p85"; ++ regulator-min-microvolt = <2856000>; ++ regulator-max-microvolt = <3104000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l8c_1p8: ldo8 { ++ regulator-name = "vreg_l8c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l9c_2p96: ldo9 { ++ regulator-name = "vreg_l9c_2p96"; ++ regulator-min-microvolt = <2704000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l10c_3p0: ldo10 { ++ regulator-name = "vreg_l10c_3p0"; ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l11c_3p3: ldo11 { ++ regulator-name = "vreg_l11c_3p3"; ++ regulator-min-microvolt = <3296000>; ++ regulator-max-microvolt = <3296000>; ++ regulator-initial-mode = ; ++ regulator-always-on; ++ }; ++ ++ vreg_s8c_1p3: smps8 { ++ regulator-name = "vreg_s8c_1p3"; ++ regulator-min-microvolt = <1352000>; ++ regulator-max-microvolt = <1352000>; ++ regulator-initial-mode = ; ++ }; ++ }; ++}; ++ ++&adsp { ++ firmware-name = "postmarketos/adsp.mbn"; ++ status = "okay"; ++}; ++ ++&cdsp { ++ firmware-name = "postmarketos/cdsp.mbn"; ++ status = "okay"; ++}; ++ ++&dispcc { ++ status = "disabled"; ++}; ++ ++&gmu { ++ status = "okay"; ++}; ++ ++&gpi_dma0 { ++ status = "okay"; ++}; ++ ++&gpi_dma1 { ++ status = "okay"; ++}; ++ ++&gpi_dma2 { ++ status = "okay"; ++}; ++ ++&gpu { ++ status = "disabled"; ++}; ++ ++&i2c1 { ++ status = "okay"; ++ ++ nfc@28 { ++ compatible = "nxp,nxp-nci-i2c"; ++ reg = <0x28>; ++ ++ interrupt-parent = <&tlmm>; ++ interrupts = <0x6f IRQ_TYPE_LEVEL_HIGH>; ++ ++ enable-gpios = <&tlmm 83 GPIO_ACTIVE_HIGH>, ++ <&tlmm 110 GPIO_ACTIVE_HIGH>; ++ }; ++}; ++ ++&i2c5 { ++ status = "okay"; ++ ++ charger@66 { ++ compatible = "ti,bq25980"; ++ status = "ok"; ++ reg = <0x66>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <0x0f 0x00>; ++ monitored-battery = <&battery>; ++ }; ++}; ++ ++&i2c16 { ++ status = "okay"; ++ ++ smart_battery: fuel-gauge@55 { ++ compatible = "ti,bq27541"; ++ reg = <0x55>; ++ monitored-battery = <&battery>; ++ }; ++}; ++ ++&i2c13 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ touchscreen@48 { ++ compatible = "samsung,s6sy761"; ++ reg = <0x48>; ++ interrupt-parent = <&tlmm>; ++ interrupts = <0x27 0x2008>; ++ ++ vdd-supply = <&vreg_l13a_ts_3p0>; ++ avdd-supply = <&vreg_l1c_1p8>; ++ ++ touchscreen-size-x = <1440>; ++ touchscreen-size-y = <3168>; ++ ++ pinctrl-names = "default", "suspend"; ++ pinctrl-0 = <&ts_int_active>; ++ pinctrl-1 = <&ts_rst_suspend>; ++ }; ++}; ++ ++&mdss { ++ status = "okay"; ++}; ++ ++&slpi { ++ firmware-name = "postmarketos/slpi.mbn"; ++ status = "okay"; ++}; ++ ++&pcie0 { ++ status = "okay"; ++}; ++ ++&pcie0_phy { ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++ status = "okay"; ++ power-domains = <&qca639x>; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&pcie1_phy { ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++ status = "okay"; ++}; ++ ++&pcie2 { ++ status = "okay"; ++}; ++ ++&pcie2_phy { ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++ status = "okay"; ++}; ++ ++&pm8150_gpios { ++ vol_up_n: vol-up-n-state { ++ pins = "gpio6"; ++ function = "normal"; ++ power-source = <1>; ++ input-enable; ++ bias-pull-up; ++ }; ++ ++ vol_down_n: vol-down-n-state { ++ pins = "gpio7"; ++ function = "normal"; ++ power-source = <1>; ++ input-enable; ++ bias-pull-up; ++ }; ++}; ++ ++&pon_pwrkey { ++ status = "okay"; ++}; ++ ++&pon_resin { ++ linux,code = ; ++ status = "okay"; ++}; ++ ++&qup_spi4_cs_gpio { ++ drive-strength = <6>; ++ bias-disable; ++}; ++ ++&qup_spi4_data_clk { ++ drive-strength = <6>; ++ bias-disable; ++}; ++ ++&qupv3_id_0 { ++ status = "okay"; ++}; ++ ++&qupv3_id_1 { ++ status = "okay"; ++}; ++ ++&qupv3_id_2 { ++ status = "okay"; ++}; ++ ++&tlmm { ++ gpio-reserved-ranges = <28 4>, <40 4>; ++ ++ bt_en_active: bt-default-state { ++ bt-en { ++ pins = "gpio21"; ++ function = "gpio"; ++ ++ drive-strength = <16>; ++ output-low; ++ bias-pull-up; ++ }; ++ }; ++ ++ bt_en_sleep: bt-sleep-state { ++ pins = "gpio21"; ++ function = "gpio"; ++ ++ drive-strength = <0x02>; ++ output-low; ++ bias-pull-down; ++ }; ++ ++ wlan_en_active: wlan-default-state { ++ wlan-en { ++ pins = "gpio20"; ++ function = "gpio"; ++ ++ drive-strength = <16>; ++ output-low; ++ bias-pull-up; ++ }; ++ }; ++ ++ wlan_en_sleep: wlan-sleep-state { ++ pins = "gpio20"; ++ function = "gpio"; ++ ++ drive-strength = <16>; ++ output-low; ++ bias-pull-down; ++ }; ++ ++ ts_int_active: ts-int-active-state { ++ pins = "gpio38", "gpio39"; ++ function = "gpio"; ++ drive-strength = <2>; ++ bias-pull-up; ++ }; ++ ++ ts_rst_suspend: ts-rst-suspend { ++ pins = "gpio38"; ++ function = "gpio"; ++ drive-strength = <0x02>; ++ bias-pull-down; ++ }; ++}; ++ ++&uart6 { ++ status = "okay"; ++ ++ bluetooth { ++ compatible = "qcom,qca6390-bt"; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&bt_en_active>; ++ pinctrl-1 = <&bt_en_sleep>; ++ ++ power-domains = <&qca639x>; ++ enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; ++ }; ++}; ++ ++&ufs_mem_hc { ++ vcc-supply = <&vreg_l17a_3p0>; ++ vcc-max-microamp = <800000>; ++ vccq-supply = <&vreg_l6a_1p2>; ++ vccq-max-microamp = <800000>; ++ vccq2-supply = <&vreg_s4a_1p8>; ++ vccq2-max-microamp = <800000>; ++ status = "okay"; ++}; ++ ++&ufs_mem_phy { ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++ status = "okay"; ++}; ++ ++&usb_1 { ++ /* USB 2.0 only */ ++ qcom,select-utmi-as-pipe-clk; ++ status = "okay"; ++}; ++ ++&usb_1_dwc3 { ++ dr_mode = "peripheral"; ++ maximum-speed = "high-speed"; ++ /* Remove USB3 phy */ ++ phys = <&usb_1_hsphy>; ++ phy-names = "usb2-phy"; ++}; ++ ++&usb_1_hsphy { ++ vdda-pll-supply = <&vreg_l5a_0p88>; ++ vdda18-supply = <&vreg_l12a_1p8>; ++ vdda33-supply = <&vreg_l2a_3p1>; ++ status = "okay"; ++}; ++ ++&usb_2 { ++ status = "okay"; ++}; ++ ++&usb_2_dwc3 { ++ dr_mode = "host"; ++}; ++ ++&usb_2_hsphy { ++ status = "okay"; ++ ++ vdda-pll-supply = <&vreg_l5a_0p88>; ++ vdda33-supply = <&vreg_l2a_3p1>; ++ vdda18-supply = <&vreg_l12a_1p8>; ++}; ++ ++&usb_2_qmpphy { ++ status = "okay"; ++ ++ vdda-phy-supply = <&vreg_l9a_1p2>; ++ vdda-pll-supply = <&vreg_l18a_0p92>; ++}; ++ ++&venus { ++ firmware-name = "postmarketos/venus.mbn"; ++ status = "okay"; ++}; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0016-arm64-dts-qcom-sm8250-xiaomi-elish-add-keyboard-supp.patch b/patch/kernel/archive/sm8250-6.18/0016-arm64-dts-qcom-sm8250-xiaomi-elish-add-keyboard-supp.patch new file mode 100644 index 000000000000..8815c6953024 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0016-arm64-dts-qcom-sm8250-xiaomi-elish-add-keyboard-supp.patch @@ -0,0 +1,77 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Sat, 2 Dec 2023 19:08:14 +0800 +Subject: arm64: dts: qcom: sm8250-xiaomi-elish: add keyboard support + +--- + arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi | 40 ++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +@@ -748,6 +748,15 @@ fuel-gauge@55 { + }; + }; + ++&lpass_tlmm { ++ keyboard_en_state: keyboard-default-state { ++ pins = "gpio9"; ++ function = "i2s1_data"; ++ drive-strength = <8>; ++ bias-pull-up; ++ }; ++}; ++ + &mdss { + status = "okay"; + }; +@@ -1065,6 +1074,14 @@ dout-pins { + bias-disable; + }; + }; ++ ++ keyboard_vdd_pin: keyboard-vdd-state { ++ pins = "gpio127"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ output-high; ++ }; + }; + + &uart6 { +@@ -1108,6 +1125,29 @@ &usb_1_dwc3_hs_out { + remote-endpoint = <&pm8150b_hs_in>; + }; + ++&usb_2 { ++ /* USB 2.0 only */ ++ qcom,select-utmi-as-pipe-clk; ++ status = "okay"; ++}; ++ ++&usb_2_dwc3 { ++ dr_mode = "host"; ++ maximum-speed = "high-speed"; ++ /* Remove USB3 phy */ ++ phys = <&usb_2_hsphy>; ++ phy-names = "usb2-phy"; ++}; ++ ++&usb_2_hsphy { ++ vdda-pll-supply = <&vreg_l5a_0p88>; ++ vdda18-supply = <&vreg_l12a_1p8>; ++ vdda33-supply = <&vreg_l2a_3p1>; ++ status = "okay"; ++ pinctrl-0 = <&keyboard_en_state>, <&keyboard_vdd_pin>; ++ pinctrl-names = "default"; ++}; ++ + &ufs_mem_hc { + vcc-supply = <&vreg_l17a_3p0>; + vcc-max-microamp = <800000>; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0017-arm64-dts-qcom-sm8250-xiaomi-elish-remove-framebuffe.patch b/patch/kernel/archive/sm8250-6.18/0017-arm64-dts-qcom-sm8250-xiaomi-elish-remove-framebuffe.patch new file mode 100644 index 000000000000..dd4ef76e0e3f --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0017-arm64-dts-qcom-sm8250-xiaomi-elish-remove-framebuffe.patch @@ -0,0 +1,51 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Mon, 11 Mar 2024 23:27:42 +0800 +Subject: arm64: dts: qcom: sm8250-xiaomi-elish: remove framebuffer initialized + by xbl + +--- + arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi | 20 ---------- + 1 file changed, 20 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250-xiaomi-elish-common.dtsi +@@ -36,21 +36,6 @@ aliases { + serial0 = &uart6; + }; + +- chosen { +- #address-cells = <2>; +- #size-cells = <2>; +- ranges; +- +- framebuffer: framebuffer@9c000000 { +- compatible = "simple-framebuffer"; +- reg = <0x0 0x9c000000 0x0 0x2300000>; +- width = <1600>; +- height = <2560>; +- stride = <(1600 * 4)>; +- format = "a8r8g8b8"; +- }; +- }; +- + battery_l: battery-l { + compatible = "simple-battery"; + voltage-min-design-microvolt = <3870000>; +@@ -207,11 +192,6 @@ cdsp_secure_heap: cdsp-secure-heap@8e100000 { + no-map; + }; + +- cont_splash_mem: cont-splash@9c000000 { +- reg = <0x0 0x9c000000 0x0 0x2300000>; +- no-map; +- }; +- + ramoops@b0000000 { + compatible = "ramoops"; + reg = <0x0 0xb0000000 0x0 0x400000>; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0019-input-nt36xxx-Enable-pen-support.patch b/patch/kernel/archive/sm8250-6.18/0019-input-nt36xxx-Enable-pen-support.patch new file mode 100644 index 000000000000..9818a190c7e5 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0019-input-nt36xxx-Enable-pen-support.patch @@ -0,0 +1,118 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: map220v +Date: Tue, 28 Nov 2023 15:28:53 +0300 +Subject: input: nt36xxx: Enable pen support + +--- + drivers/input/touchscreen/nt36523/nt36xxx.c | 47 +++++++++- + drivers/input/touchscreen/nt36523/nt36xxx.h | 1 + + drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c | 1 + + 3 files changed, 45 insertions(+), 4 deletions(-) + +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.c b/drivers/input/touchscreen/nt36523/nt36xxx.c +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/nt36523/nt36xxx.c ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.c +@@ -1173,6 +1173,43 @@ static int8_t nvt_ts_check_chip_ver_trim(uint32_t chip_ver_trim_addr) + return 0; + } + ++int32_t disable_pen_input_device(bool disable) { ++ uint8_t buf[8] = {0}; ++ int32_t ret = 0; ++ ++ NVT_LOG("++\n"); ++ if (!bTouchIsAwake || !ts) { ++ NVT_LOG("touch suspend, stop set pen state %s", disable ? "DISABLE" : "ENABLE"); ++ goto nvt_set_pen_enable_out; ++ } ++ ++ msleep(35); ++ disable = (!(ts->pen_input_dev_enable) || ts->pen_is_charge) ? true : disable; ++ ++ //---set xdata index to EVENT BUF ADDR--- ++ ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); ++ if (ret < 0) { ++ NVT_ERR("Set event buffer index fail!\n"); ++ goto nvt_set_pen_enable_out; ++ } ++ ++ buf[0] = EVENT_MAP_HOST_CMD; ++ buf[1] = 0x7B; ++ buf[2] = !!disable; ++ ret = CTP_SPI_WRITE(ts->client, buf, 3); ++ if (ret < 0) { ++ NVT_ERR("set pen %s failed!\n", disable ? "DISABLE" : "ENABLE"); ++ goto nvt_set_pen_enable_out; ++ } ++ NVT_LOG("pen charge state is %s, %s pen input device\n", ++ ts->pen_is_charge ? "ENABLE" : "DISABLE", ++ disable ? "DISABLE" : "ENABLE"); ++ ++nvt_set_pen_enable_out: ++ NVT_LOG("--\n"); ++ return ret; ++} ++ + static void nvt_suspend_work(struct work_struct *work) + { + struct nvt_ts_data *ts_core = container_of(work, struct nvt_ts_data, suspend_work); +@@ -1437,11 +1474,11 @@ static int32_t nvt_ts_probe(struct spi_device *client) + init_completion(&ts->dev_pm_suspend_completion); + ts->fw_debug = false; + +-#ifdef CONFIG_FACTORY_BUILD ++//#ifdef CONFIG_FACTORY_BUILD + ts->pen_input_dev_enable = 1; +-#else +- ts->pen_input_dev_enable = 0; +-#endif ++//#else ++// ts->pen_input_dev_enable = 0; ++//#endif + + #if BOOT_UPDATE_FIRMWARE + nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); +@@ -1701,6 +1738,7 @@ static int32_t nvt_ts_suspend(struct device *dev) + + if (ts->pen_input_dev_enable) { + NVT_LOG("if enable pen,will close it"); ++ disable_pen_input_device(true); + } + + if (ts->db_wakeup) { +@@ -1798,6 +1836,7 @@ static int32_t nvt_ts_resume(struct device *dev) + + mutex_unlock(&ts->lock); + ++ disable_pen_input_device(false); + if (likely(ts->ic_state == NVT_IC_RESUME_IN)) { + ts->ic_state = NVT_IC_RESUME_OUT; + } else { +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.h b/drivers/input/touchscreen/nt36523/nt36xxx.h +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/nt36523/nt36xxx.h ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.h +@@ -235,6 +235,7 @@ int32_t nvt_read_pid(void); + bool nvt_get_dbgfw_status(void); + int32_t nvt_set_pocket_palm_switch(uint8_t pocket_palm_switch); + void Boot_Update_Firmware(struct work_struct *work); ++int32_t disable_pen_input_device(bool disable); + #if NVT_TOUCH_ESD_PROTECT + extern void nvt_esd_check_enable(uint8_t enable); + #endif /* #if NVT_TOUCH_ESD_PROTECT */ +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c b/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c ++++ b/drivers/input/touchscreen/nt36523/nt36xxx_fw_update.c +@@ -851,6 +851,7 @@ void Boot_Update_Firmware(struct work_struct *work) + { + mutex_lock(&ts->lock); + nvt_update_firmware(ts->fw_name); ++ disable_pen_input_device(false); + nvt_get_fw_info(); + mutex_unlock(&ts->lock); + } +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0020-nt36xxx-add-pen-input-resolution.patch b/patch/kernel/archive/sm8250-6.18/0020-nt36xxx-add-pen-input-resolution.patch new file mode 100644 index 000000000000..58cab04a837b --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0020-nt36xxx-add-pen-input-resolution.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nicola Guerrera +Date: Thu, 14 Nov 2024 22:57:02 +0100 +Subject: nt36xxx: add pen input resolution + +--- + drivers/input/touchscreen/nt36523/nt36xxx.c | 15 +++++++--- + drivers/input/touchscreen/nt36523/nt36xxx.h | 4 ++- + 2 files changed, 14 insertions(+), 5 deletions(-) + +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.c b/drivers/input/touchscreen/nt36523/nt36xxx.c +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/nt36523/nt36xxx.c ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.c +@@ -1421,14 +1421,21 @@ static int32_t nvt_ts_probe(struct spi_device *client) + ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); + ts->pen_input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + ++ int x_max, y_max; ++ + if (ts->wgp_stylus) { +- input_set_abs_params(ts->pen_input_dev, ABS_X, 0, ts->abs_x_max * 2 - 1, 0, 0); +- input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, ts->abs_y_max * 2 - 1, 0, 0); ++ x_max = ts->abs_x_max * 2 - 1; ++ y_max = ts->abs_y_max * 2 - 1; + } else { +- input_set_abs_params(ts->pen_input_dev, ABS_X, 0, ts->abs_x_max - 1, 0, 0); +- input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, ts->abs_y_max - 1, 0, 0); ++ x_max = ts->abs_x_max - 1; ++ y_max = ts->abs_y_max - 1; + } + ++ input_set_abs_params(ts->pen_input_dev, ABS_X, 0, x_max, 0, 0); ++ input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, y_max , 0, 0); ++ input_abs_set_res(ts->pen_input_dev, ABS_X, x_max / PANEL_DEFAULT_WIDTH_MM); ++ input_abs_set_res(ts->pen_input_dev, ABS_Y, y_max / PANEL_DEFAULT_HEIGHT_MM); ++ + input_set_abs_params(ts->pen_input_dev, ABS_PRESSURE, 0, PEN_PRESSURE_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_DISTANCE, 0, PEN_DISTANCE_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_TILT_X, PEN_TILT_MIN, PEN_TILT_MAX, 0, 0); +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.h b/drivers/input/touchscreen/nt36523/nt36xxx.h +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/nt36523/nt36xxx.h ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.h +@@ -70,7 +70,9 @@ extern const uint16_t touch_key_array[TOUCH_KEY_NUM]; + #define PEN_DISTANCE_MAX (1) + #define PEN_TILT_MIN (-60) + #define PEN_TILT_MAX (60) +- ++//---for pen resolution--- ++#define PANEL_DEFAULT_WIDTH_MM 148 // 148mm ++#define PANEL_DEFAULT_HEIGHT_MM 237 // 237mm + /* Enable only when module have tp reset pin and connected to host */ + #define NVT_TOUCH_SUPPORT_HW_RST 0 + +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0021-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Restore.patch b/patch/kernel/archive/sm8250-6.18/0021-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Restore.patch new file mode 100644 index 000000000000..126477e153d0 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0021-arm64-dts-qcom-sm8250-oneplus-instantnoodlep-Restore.patch @@ -0,0 +1,217 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Denys Vitali +Date: Fri, 22 Nov 2024 15:50:44 +0100 +Subject: arm64: dts: qcom: sm8250-oneplus-instantnoodlep: Restore Wi-Fi / BT + functionality + +--- + arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts | 131 ++++++++-- + 1 file changed, 110 insertions(+), 21 deletions(-) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts b/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts ++++ b/arch/arm64/boot/dts/qcom/sm8250-oneplus-instantnoodlep.dts +@@ -78,6 +78,67 @@ vol-down { + gpio-key,wakeup; + }; + }; ++ ++ qca6390-pmu { ++ compatible = "qcom,qca6390-pmu"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&bt_en_state>, <&wlan_en_state>; ++ ++ vddaon-supply = <&vreg_s6a_0p95>; ++ vddpmu-supply = <&vreg_s2f_0p95>; ++ vddrfa0p95-supply = <&vreg_s2f_0p95>; ++ vddrfa1p3-supply = <&vreg_s8c_1p3>; ++ vddrfa1p9-supply = <&vreg_s5a_1p9>; ++ vddpcie1p3-supply = <&vreg_s8c_1p3>; ++ vddpcie1p9-supply = <&vreg_s5a_1p9>; ++ vddio-supply = <&vreg_s4a_1p8>; ++ ++ wlan-enable-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; ++ bt-enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; ++ ++ regulators { ++ vreg_pmu_rfa_cmn: ldo0 { ++ regulator-name = "vreg_pmu_rfa_cmn"; ++ }; ++ ++ vreg_pmu_aon_0p59: ldo1 { ++ regulator-name = "vreg_pmu_aon_0p59"; ++ }; ++ ++ vreg_pmu_wlcx_0p8: ldo2 { ++ regulator-name = "vreg_pmu_wlcx_0p8"; ++ }; ++ ++ vreg_pmu_wlmx_0p85: ldo3 { ++ regulator-name = "vreg_pmu_wlmx_0p85"; ++ }; ++ ++ vreg_pmu_btcmx_0p85: ldo4 { ++ regulator-name = "vreg_pmu_btcmx_0p85"; ++ }; ++ ++ vreg_pmu_rfa_0p8: ldo5 { ++ regulator-name = "vreg_pmu_rfa_0p8"; ++ }; ++ ++ vreg_pmu_rfa_1p2: ldo6 { ++ regulator-name = "vreg_pmu_rfa_1p2"; ++ }; ++ ++ vreg_pmu_rfa_1p7: ldo7 { ++ regulator-name = "vreg_pmu_rfa_1p7"; ++ }; ++ ++ vreg_pmu_pcie_0p9: ldo8 { ++ regulator-name = "vreg_pmu_pcie_0p9"; ++ }; ++ ++ vreg_pmu_pcie_1p8: ldo9 { ++ regulator-name = "vreg_pmu_pcie_1p8"; ++ }; ++ }; ++ }; + + reserved-memory { + removed_mem: memory@80b00000 { +@@ -192,7 +253,7 @@ vreg_s4a_1p8: vreg-s4a-1p8 { + qca639x: qca639x { + compatible = "qcom,qca6390"; + #power-domain-cells = <0>; +- ++ + vddaon-supply = <&vreg_s6a_0p95>; + vddpmu-supply = <&vreg_s2f_0p95>; + vddrfa1-supply = <&vreg_s2f_0p95>; +@@ -203,9 +264,9 @@ qca639x: qca639x { + vddio-supply = <&vreg_s4a_1p8>; + + pinctrl-names = "default", "sleep"; +- pinctrl-0 = <&wlan_en_active>; ++ pinctrl-0 = <&wlan_en_state>; + pinctrl-1 = <&wlan_en_sleep>; +- ++ + wlan-en-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; + }; + +@@ -390,6 +451,7 @@ vreg_s5a_1p9: smps5 { + regulator-initial-mode = ; + }; + ++ + vreg_s6a_0p95: smps6 { + regulator-name = "vreg_s6a_0p95"; + regulator-min-microvolt = <920000>; +@@ -614,15 +676,32 @@ &slpi { + status = "okay"; + }; + ++ + &pcie0 { + status = "okay"; + }; + + &pcie0_phy { ++ status = "okay"; + vdda-phy-supply = <&vreg_l5a_0p88>; + vdda-pll-supply = <&vreg_l9a_1p2>; +- status = "okay"; +- power-domains = <&qca639x>; ++}; ++ ++&pcieport0 { ++ wifi@0 { ++ compatible = "pci17cb,1101"; ++ reg = <0x10000 0x0 0x0 0x0 0x0>; ++ ++ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; ++ vddaon-supply = <&vreg_pmu_aon_0p59>; ++ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; ++ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; ++ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; ++ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; ++ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; ++ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; ++ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; ++ }; + }; + + &pcie1 { +@@ -697,7 +776,7 @@ &qupv3_id_2 { + &tlmm { + gpio-reserved-ranges = <28 4>, <40 4>; + +- bt_en_active: bt-default-state { ++ bt_en_state: bt-default-state { + bt-en { + pins = "gpio21"; + function = "gpio"; +@@ -717,7 +796,7 @@ bt_en_sleep: bt-sleep-state { + bias-pull-down; + }; + +- wlan_en_active: wlan-default-state { ++ wlan_en_state: wlan-default-state { + wlan-en { + pins = "gpio20"; + function = "gpio"; +@@ -752,20 +831,6 @@ ts_rst_suspend: ts-rst-suspend { + }; + }; + +-&uart6 { +- status = "okay"; +- +- bluetooth { +- compatible = "qcom,qca6390-bt"; +- pinctrl-names = "default", "sleep"; +- pinctrl-0 = <&bt_en_active>; +- pinctrl-1 = <&bt_en_sleep>; +- +- power-domains = <&qca639x>; +- enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; +- }; +-}; +- + &ufs_mem_hc { + vcc-supply = <&vreg_l17a_3p0>; + vcc-max-microamp = <800000>; +@@ -830,3 +895,27 @@ &venus { + firmware-name = "postmarketos/venus.mbn"; + status = "okay"; + }; ++ ++ ++&uart6 { ++ status = "okay"; ++ ++ bluetooth { ++ compatible = "qcom,qca6390-bt"; ++ ++ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; ++ vddaon-supply = <&vreg_pmu_aon_0p59>; ++ vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; ++ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; ++ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; ++ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; ++ }; ++}; ++ ++&qup_i2c7_default { ++ status = "disabled"; ++}; ++ ++&qup_spi7_data_clk { ++ status = "disabled"; ++}; +\ No newline at end of file +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0023-Asoc-wm_adsp-Use-xiaomi-elish-firmware-name.patch b/patch/kernel/archive/sm8250-6.18/0023-Asoc-wm_adsp-Use-xiaomi-elish-firmware-name.patch new file mode 100644 index 000000000000..ab90092465d8 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0023-Asoc-wm_adsp-Use-xiaomi-elish-firmware-name.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: amazingfate +Date: Tue, 24 Oct 2023 01:01:46 +0800 +Subject: Asoc: wm_adsp: Use xiaomi-elish firmware name + +--- + sound/soc/codecs/wm_adsp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +index 111111111111..222222222222 100644 +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -828,7 +828,7 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, + } + } else if (asoc_component_prefix) { + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, +- cirrus_dir, NULL, ++ cirrus_dir, "xiaomi-elish", + NULL, "wmfw")) { + adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename); + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0024-input-nt36523-short-the-firmware-download-delay-from.patch b/patch/kernel/archive/sm8250-6.18/0024-input-nt36523-short-the-firmware-download-delay-from.patch new file mode 100644 index 000000000000..856c089f81e6 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0024-input-nt36523-short-the-firmware-download-delay-from.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: amazingfate +Date: Thu, 26 Oct 2023 13:13:34 +0800 +Subject: input: nt36523: short the firmware download delay from 14s to 4s + +--- + drivers/input/touchscreen/nt36523/nt36xxx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/input/touchscreen/nt36523/nt36xxx.c b/drivers/input/touchscreen/nt36523/nt36xxx.c +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/nt36523/nt36xxx.c ++++ b/drivers/input/touchscreen/nt36523/nt36xxx.c +@@ -1496,7 +1496,7 @@ static int32_t nvt_ts_probe(struct spi_device *client) + } + INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware); + // please make sure boot update start after display reset(RESX) sequence +- queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000)); ++ queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(4000)); + #endif + + NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT); +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0027-Input-touchscreen-add-Synaptics-TCM-oncell-S3908.patch b/patch/kernel/archive/sm8250-6.18/0027-Input-touchscreen-add-Synaptics-TCM-oncell-S3908.patch new file mode 100644 index 000000000000..a7a8a2fd8f78 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0027-Input-touchscreen-add-Synaptics-TCM-oncell-S3908.patch @@ -0,0 +1,640 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Caleb Connolly +Date: Sun, 30 Jun 2024 20:36:30 +0200 +Subject: Input: touchscreen: add Synaptics TCM oncell S3908 + +The TCM oncell is the next generation of Synaptics touchscreen ICs. +These run a very featured firmware with a reasonably well defined API. +It is however entirely incompatible with the existing RMI4 interface. + +Unfortunately, no public datasheet for the interface seems to be +available, instead this driver was created through a combination of +vendor drivers and trial and error. + +The firmware interface implies support for defining the exact bit +encoding of the touch reports, however on the S3908 chip + firmware +found in the OnePlus 8t the TCM_SET_TOUCH_REPORT_CONFIG command appears +to be unsupported. + +Co-developed-by: Frieder Hannenheim +Signed-off-by: Frieder Hannenheim +Signed-off-by: Caleb Connolly +--- + drivers/input/touchscreen/Kconfig | 11 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/synaptics_tcm_oncell.c | 570 ++++++++++ + 3 files changed, 582 insertions(+) + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -504,6 +504,17 @@ config TOUCHSCREEN_S6SY761 + To compile this driver as module, choose M here: the + module will be called s6sy761. + ++config TOUCHSCREEN_SYNAPTICS_TCM_ONCELL ++ tristate "Synaptics TCM Oncell Touchscreen driver" ++ depends on I2C ++ help ++ Say Y if you have the Synaptics S3908 TCM Oncell ++ ++ If unsure, say N ++ ++ To compile this driver as module, choose M here: the ++ module will be called synaptics_tcm_oncell. ++ + config TOUCHSCREEN_GUNZE + tristate "Gunze AHL-51S touchscreen" + select SERIO +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -85,6 +85,7 @@ obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o + obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o + obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o + obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o ++obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_ONCELL) += synaptics_tcm_oncell.o + obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o +diff --git a/drivers/input/touchscreen/synaptics_tcm_oncell.c b/drivers/input/touchscreen/synaptics_tcm_oncell.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/touchscreen/synaptics_tcm_oncell.c +@@ -0,0 +1,570 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Driver for Synaptics TCM Oncell Touchscreens ++ * ++ * Copyright (c) 2024 Frieder Hannenheim ++ * Copyright (c) 2024 Caleb Connolly ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The TCM oncell interface uses a command byte, which may be followed by additional ++ * data. The packet format is defined in the tcm_cmd struct. ++ * ++ * The following list only defines commands that are used in this driver (and their ++ * counterparts for context). Vendor reference implementations can be found at ++ * https://github.com/LineageOS/android_kernel_oneplus_sm8250/tree/ee0a7ee1939ffd53000e42051caf8f0800defb27/drivers/input/touchscreen/synaptics_tcm ++ */ ++ ++/* ++ * Request information about the chip. We don't send this command explicitly as ++ * the controller automatically sends this information when starting up. ++ */ ++#define TCM_IDENTIFY 0x02 ++ ++/* Enable/disable reporting touch inputs */ ++#define TCM_ENABLE_REPORT 0x05 ++#define TCM_DISABLE_REPORT 0x06 ++ ++/* ++ * After powering on, we send this to exit the bootloader mode and run the main ++ * firmware. ++ */ ++#define TCM_RUN_APPLICATION_FIRMWARE 0x14 ++ ++/* ++ * Reports information about the vendor provided application firmware. This is ++ * also used to determine when the firmware has finished booting. ++ */ ++#define TCM_GET_APPLICATION_INFO 0x20 ++ ++#define MODE_APPLICATION 0x01 ++ ++#define APP_STATUS_OK 0x00 ++#define APP_STATUS_BOOTING 0x01 ++#define APP_STATUS_UPDATING 0x02 ++ ++/* status codes */ ++#define REPORT_IDLE 0x00 ++#define REPORT_OK 0x01 ++#define REPORT_BUSY 0x02 ++#define REPORT_CONTINUED_READ 0x03 ++#define REPORT_RECEIVE_BUFFER_OVERFLOW 0x0c ++#define REPORT_PREVIOUS_COMMAND_PENDING 0x0d ++#define REPORT_NOT_IMPLEMENTED 0x0e ++#define REPORT_ERROR 0x0f ++ ++/* report types */ ++#define REPORT_IDENTIFY 0x10 ++#define REPORT_TOUCH 0x11 ++#define REPORT_DELTA 0x12 ++#define REPORT_RAW 0x13 ++#define REPORT_DEBUG 0x14 ++#define REPORT_LOG 0x1d ++#define REPORT_TOUCH_HOLD 0x20 ++#define REPORT_INVALID 0xff ++ ++struct tcm_message_header { ++ u8 marker; ++ u8 code; ++ __le16 length; ++} __packed; ++ ++struct tcm_cmd { ++ u8 cmd; ++ __le16 length; ++ u8 data[]; ++}; ++ ++struct tcm_identification { ++ struct tcm_message_header header; ++ u8 version; ++ u8 mode; ++ char part_number[16]; ++ u8 build_id[4]; ++ u8 max_write_size[2]; ++} __packed; ++ ++struct tcm_app_info { ++ struct tcm_message_header header; ++ u8 version[2]; ++ __le16 status; ++ u8 static_config_size[2]; ++ u8 dynamic_config_size[2]; ++ u8 app_config_start_write_block[2]; ++ u8 app_config_size[2]; ++ u8 max_touch_report_config_size[2]; ++ u8 max_touch_report_payload_size[2]; ++ char customer_config_id[16]; ++ __le16 max_x; ++ __le16 max_y; ++ u8 max_objects[2]; ++ u8 num_of_buttons[2]; ++ u8 num_of_image_rows[2]; ++ u8 num_of_image_cols[2]; ++ u8 has_hybrid_data[2]; ++} __packed; ++ ++struct tcm_data { ++ struct i2c_client *client; ++ struct regmap *regmap; ++ struct input_dev *input; ++ struct gpio_desc *reset_gpio; ++ struct completion response; ++ struct touchscreen_properties props; ++ struct regulator_bulk_data supplies[2]; ++ ++ /* annoying state */ ++ u16 buf_size; ++ char buf[256]; ++}; ++ ++static int tcm_send_cmd(struct tcm_data *tcm, struct tcm_cmd *cmd) ++{ ++ struct i2c_client *client = tcm->client; ++ struct i2c_msg msg; ++ int ret; ++ ++ dev_dbg(&client->dev, "sending command %#x\n", cmd->cmd); ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 1 + cmd->length; ++ msg.buf = (u8 *)cmd; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret == 1) ++ return 0; ++ else if (ret < 0) ++ return ret; ++ else ++ return -EIO; ++} ++ ++static int tcm_send_cmd_noargs(struct tcm_data *tcm, u8 cmd) ++{ ++ struct tcm_cmd c = { ++ .cmd = cmd, ++ .length = 0, ++ }; ++ ++ return tcm_send_cmd(tcm, &c); ++} ++ ++static int tcm_recv_report(struct tcm_data *tcm, ++ void *buf, size_t length) ++{ ++ struct i2c_client *client = tcm->client; ++ struct i2c_msg msg; ++ int ret; ++ ++ msg.addr = client->addr; ++ msg.flags = I2C_M_RD; ++ msg.len = length; ++ msg.buf = buf; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret == 1) ++ return 0; ++ else if (ret < 0) ++ return ret; ++ else ++ return -EIO; ++} ++ ++static int tcm_read_message(struct tcm_data *tcm, u8 cmd, void *buf, size_t length) ++{ ++ int ret; ++ ++ reinit_completion(&tcm->response); ++ ret = tcm_send_cmd_noargs(tcm, cmd); ++ if (ret) ++ return ret; ++ ++ ret = wait_for_completion_timeout(&tcm->response, msecs_to_jiffies(1000)); ++ if (ret == 0) ++ return -ETIMEDOUT; ++ ++ if (buf) { ++ if (length > tcm->buf_size) { ++ dev_warn(&tcm->client->dev, "expected %zu bytes, got %u\n", ++ length, tcm->buf_size); ++ } ++ length = min(tcm->buf_size, length); ++ memcpy(buf, tcm->buf, length); ++ } ++ ++ return 0; ++} ++ ++static void tcm_power_off(void *data) ++{ ++ struct tcm_data *tcm = data; ++ ++ disable_irq(tcm->client->irq); ++ regulator_bulk_disable(ARRAY_SIZE(tcm->supplies), tcm->supplies); ++} ++ ++static int tcm_input_open(struct input_dev *dev) ++{ ++ struct tcm_data *tcm = input_get_drvdata(dev); ++ ++ return i2c_smbus_write_byte(tcm->client, TCM_ENABLE_REPORT); ++} ++ ++static void tcm_input_close(struct input_dev *dev) ++{ ++ struct tcm_data *tcm = input_get_drvdata(dev); ++ int ret; ++ ++ ret = i2c_smbus_write_byte(tcm->client, TCM_DISABLE_REPORT); ++ if (ret) ++ dev_err(&tcm->client->dev, "failed to turn off sensing\n"); ++} ++ ++/* ++ * The default report config looks like this: ++ * ++ * a5 01 80 00 11 08 1e 08 0f 01 04 01 06 04 07 04 ++ * 08 0c 09 0c 0a 08 0b 08 0c 08 0d 10 0e 10 03 00 ++ * 00 00 ++ * ++ * a5 01 80 00 - HEADER + length ++ * ++ * 11 08 - TOUCH_FRAME_RATE (8 bits) ++ * 30 08 - UNKNOWN (8 bits) ++ * 0f 01 - TOUCH_0D_BUTTONS_STATE (1 bit) ++ * 04 01 - TOUCH_PAD_TO_NEXT_BYTE (7 bits - padding) ++ * 06 04 - TOUCH_OBJECT_N_INDEX (4 bits) ++ * 07 04 - TOUCH_OBJECT_N_CLASSIFICATION (4 bits) ++ * 08 0c - TOUCH_OBJECT_N_X_POSITION (12 bits) ++ * 09 0c - TOUCH_OBJECT_N_Y_POSITION (12 bits) ++ * 0a 08 - TOUCH_OBJECT_N_Z (8 bits) ++ * 0b 08 - TOUCH_OBJECT_N_X_WIDTH (8 bits) ++ * 0c 08 - TOUCH_OBJECT_N_Y_WIDTH (8 bits) ++ * 0d 10 - TOUCH_OBJECT_N_TX_POSITION_TIXELS (16 bits) ?? ++ * 0e 10 - TOUCH_OBJECT_N_RX_POSITION_TIXELS (16 bits) ?? ++ * 03 00 - TOUCH_FOREACH_END (0 bits) ++ * 00 00 - TOUCH_END (0 bits) ++ * ++ * Since we only support this report config, we just hardcode the format below. ++ * To support additional report configs, we would need to parse the config and ++ * use it to parse the reports dynamically. ++ */ ++ ++struct tcm_report_point { ++ u8 unknown; ++ u8 buttons; ++ __le32 point; /* idx : 4, class : 4, x : 12, y : 12 */ ++ // u8 idx : 4; ++ // u8 classification : 4; ++ // u16 x : 12; ++ // u16 y : 12; ++ u8 z; ++ u8 width_x; ++ u8 width_y; ++ u8 tx; ++ u8 rx; ++} __packed; ++ ++static int tcm_handle_touch_report(struct tcm_data *tcm, const char *buf, size_t len) ++{ ++ const struct tcm_report_point *point; ++ /* If the input device hasn't registered yet then we can't do anything */ ++ if (!tcm->input) ++ return 0; ++ ++ buf += sizeof(struct tcm_message_header); ++ len -= sizeof(struct tcm_message_header); ++ ++ dev_dbg(&tcm->client->dev, "touch report len %zu\n", len); ++ if ((len - 3) % sizeof(*point)) ++ dev_err(&tcm->client->dev, "invalid touch report length\n"); ++ ++ buf++; /* Skip the FPS report */ ++ ++ /* We don't need to report releases because we have INPUT_MT_DROP_UNUSED */ ++ for (int i = 0; i < (len - 1) / sizeof(*point); i++) { ++ u8 major_width, minor_width; ++ u16 idx, x, y; ++ u32 _point; ++ ++ point = (struct tcm_report_point *)buf; ++ _point = le32_to_cpu(point->point); ++ ++ minor_width = point->width_x; ++ major_width = point->width_y; ++ ++ if (minor_width > major_width) ++ swap(major_width, minor_width); ++ ++ idx = _point & 0xf; ++ x = (_point >> 8) & 0xfff; ++ y = (_point >> 20) & 0xfff; ++ ++ dev_dbg(&tcm->client->dev, "touch report: idx %u x %u y %u\n", ++ idx, x, y); ++ ++ input_mt_slot(tcm->input, idx); ++ input_mt_report_slot_state(tcm->input, MT_TOOL_FINGER, true); ++ ++ touchscreen_report_pos(tcm->input, &tcm->props, x, y, true); ++ ++ input_report_abs(tcm->input, ABS_MT_TOUCH_MAJOR, major_width); ++ input_report_abs(tcm->input, ABS_MT_TOUCH_MINOR, minor_width); ++ input_report_abs(tcm->input, ABS_MT_PRESSURE, point->z); ++ ++ buf += sizeof(*point); ++ } ++ ++ input_mt_sync_frame(tcm->input); ++ input_sync(tcm->input); ++ ++ return 0; ++} ++ ++static irqreturn_t tcm_report_irq(int irq, void *data) ++{ ++ struct tcm_data *tcm = data; ++ struct tcm_message_header *header; ++ char buf[256]; ++ u16 len; ++ int ret; ++ ++ header = (struct tcm_message_header *)buf; ++ ret = tcm_recv_report(tcm, buf, sizeof(buf)); ++ if (ret) { ++ dev_err(&tcm->client->dev, "failed to read report: %d\n", ret); ++ return IRQ_HANDLED; ++ } ++ ++ switch (header->code) { ++ case REPORT_OK: ++ case REPORT_IDENTIFY: ++ case REPORT_TOUCH: ++ case REPORT_DELTA: ++ case REPORT_RAW: ++ case REPORT_DEBUG: ++ case REPORT_TOUCH_HOLD: ++ break; ++ default: ++ dev_dbg(&tcm->client->dev, "Ignoring report %#x\n", header->code); ++ return IRQ_HANDLED; ++ } ++ ++ len = le32_to_cpu(header->length); ++ ++ dev_dbg(&tcm->client->dev, "report %#x len %u\n", header->code, len); ++ print_hex_dump_bytes("report: ", DUMP_PREFIX_OFFSET, buf, ++ min(sizeof(buf), len + sizeof(*header))); ++ ++ if (len > sizeof(buf) - sizeof(*header)) { ++ dev_err(&tcm->client->dev, "report too long\n"); ++ return IRQ_HANDLED; ++ } ++ ++ /* Check if this is a read response or an indication. For indications ++ * (user touched the screen) we just parse the report directly. ++ */ ++ if (completion_done(&tcm->response) && header->code == REPORT_TOUCH) { ++ tcm_handle_touch_report(tcm, buf, len + sizeof(*header)); ++ return IRQ_HANDLED; ++ } ++ ++ tcm->buf_size = len + sizeof(*header); ++ memcpy(tcm->buf, buf, len + sizeof(*header)); ++ complete(&tcm->response); ++ ++ return IRQ_HANDLED; ++} ++ ++static int tcm_hw_init(struct tcm_data *tcm, u16 *max_x, u16 *max_y) ++{ ++ int ret; ++ struct tcm_identification id = { 0 }; ++ struct tcm_app_info app_info = { 0 }; ++ u16 status; ++ ++ /* ++ * Tell the firmware to start up. After starting it sends an IDENTIFY report, which ++ * we treat like a response to this message even though it's technically a new report. ++ */ ++ ret = tcm_read_message(tcm, TCM_RUN_APPLICATION_FIRMWARE, &id, sizeof(id)); ++ if (ret) { ++ dev_err(&tcm->client->dev, "failed to identify device: %d\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(&tcm->client->dev, "Synaptics TCM %s v%d mode %d\n", ++ id.part_number, id.version, id.mode); ++ if (id.mode != MODE_APPLICATION) { ++ /* We don't support firmware updates or anything else */ ++ dev_err(&tcm->client->dev, "Device is not in application mode\n"); ++ return -ENODEV; ++ } ++ ++ do { ++ msleep(20); ++ ret = tcm_read_message(tcm, TCM_GET_APPLICATION_INFO, &app_info, sizeof(app_info)); ++ if (ret) { ++ dev_err(&tcm->client->dev, "failed to get application info: %d\n", ret); ++ return ret; ++ } ++ status = le16_to_cpu(app_info.status); ++ } while (status == APP_STATUS_BOOTING || status == APP_STATUS_UPDATING); ++ ++ dev_dbg(&tcm->client->dev, "Application firmware v%d.%d (customer '%s') status %d\n", ++ app_info.version[0], app_info.version[1], app_info.customer_config_id, ++ status); ++ ++ *max_x = le16_to_cpu(app_info.max_x); ++ *max_y = le16_to_cpu(app_info.max_y); ++ ++ return 0; ++} ++ ++static int tcm_power_on(struct tcm_data *tcm) ++{ ++ int ret; ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(tcm->supplies), ++ tcm->supplies); ++ if (ret) ++ return ret; ++ ++ gpiod_set_value_cansleep(tcm->reset_gpio, 1); ++ usleep_range(10000, 11000); ++ gpiod_set_value_cansleep(tcm->reset_gpio, 0); ++ usleep_range(80000, 81000); ++ ++ return 0; ++} ++ ++static int tcm_probe(struct i2c_client *client) ++{ ++ struct tcm_data *tcm; ++ u16 max_x, max_y; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | ++ I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_I2C_BLOCK)) ++ return -ENODEV; ++ ++ tcm = devm_kzalloc(&client->dev, sizeof(struct tcm_data), GFP_KERNEL); ++ if (!tcm) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, tcm); ++ tcm->client = client; ++ ++ init_completion(&tcm->response); ++ ++ tcm->supplies[0].supply = "vdd"; ++ tcm->supplies[1].supply = "vcc"; ++ ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(tcm->supplies), ++ tcm->supplies); ++ if (ret) ++ return ret; ++ ++ tcm->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW); ++ ++ ret = devm_add_action_or_reset(&client->dev, tcm_power_off, ++ tcm); ++ if (ret) ++ return ret; ++ ++ ret = tcm_power_on(tcm); ++ if (ret) ++ return ret; ++ ++ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, ++ tcm_report_irq, ++ IRQF_ONESHOT, ++ "synaptics_tcm_report", tcm); ++ if (ret < 0) ++ return ret; ++ ++ ret = tcm_hw_init(tcm, &max_x, &max_y); ++ if (ret) { ++ dev_err(&client->dev, "failed to initialize hardware\n"); ++ return ret; ++ } ++ ++ tcm->input = devm_input_allocate_device(&client->dev); ++ if (!tcm->input) ++ return -ENOMEM; ++ ++ tcm->input->name = "Synaptics TCM Oncell Touchscreen"; ++ tcm->input->id.bustype = BUS_I2C; ++ tcm->input->open = tcm_input_open; ++ tcm->input->close = tcm_input_close; ++ ++ input_set_abs_params(tcm->input, ABS_MT_POSITION_X, 0, max_x, 0, 0); ++ input_set_abs_params(tcm->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0); ++ input_set_abs_params(tcm->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); ++ input_set_abs_params(tcm->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); ++ input_set_abs_params(tcm->input, ABS_MT_PRESSURE, 0, 255, 0, 0); ++ ++ touchscreen_parse_properties(tcm->input, true, &tcm->props); ++ ++ ret = input_mt_init_slots(tcm->input, 10, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); ++ if (ret) ++ return ret; ++ ++ input_set_drvdata(tcm->input, tcm); ++ ++ ret = input_register_device(tcm->input); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static const struct of_device_id syna_driver_ids[] = { ++ { ++ .compatible = "syna,s3908", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, syna_driver_ids); ++ ++static const struct i2c_device_id syna_i2c_ids[] = { ++ { "synaptics-tcm", 0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(i2c, syna_i2c_ids); ++ ++static struct i2c_driver syna_i2c_driver = { ++ .probe = tcm_probe, ++ .id_table = syna_i2c_ids, ++ .driver = { ++ .name = "synaptics-tcm", ++ .of_match_table = syna_driver_ids, ++ }, ++}; ++ ++module_i2c_driver(syna_i2c_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Frieder Hannenheim "); ++MODULE_AUTHOR("Caleb Connolly "); ++MODULE_DESCRIPTION("A driver for Synaptics TCM Oncell Touchpanels"); ++ +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0028-arm64-dts-qcom-add-OnePlus-8T-kebab.patch b/patch/kernel/archive/sm8250-6.18/0028-arm64-dts-qcom-add-OnePlus-8T-kebab.patch new file mode 100644 index 000000000000..d77c9ecc44e3 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0028-arm64-dts-qcom-add-OnePlus-8T-kebab.patch @@ -0,0 +1,975 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Caleb Connolly +Date: Sun, 30 Jun 2024 20:36:31 +0200 +Subject: arm64: dts: qcom: add OnePlus 8T (kebab) + +Initial support for USB, UFS, touchscreen, panel, wifi, and bluetooth. + +Co-developed-by: Frieder Hannenheim +Signed-off-by: Frieder Hannenheim +Signed-off-by: Caleb Connolly +--- + arch/arm64/boot/dts/qcom/sm8250-oneplus-common.dtsi | 909 ++++++++++ + arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts | 36 + + 2 files changed, 945 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-oneplus-common.dtsi +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/sm8250-oneplus-common.dtsi +@@ -0,0 +1,909 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (c) 2024, Frieder Hannenheim ++ * Copyright (c) 2024, Caleb Connolly ++ */ ++ ++/dts-v1/; ++ ++#include ++#include ++#include ++#include ++#include ++#include "sm8250.dtsi" ++#include "pm8150.dtsi" ++#include "pm8150b.dtsi" ++#include "pm8150l.dtsi" ++ ++/* removed_mem @ 0x80b00000 is bigger so we gotta shift everything up... */ ++/delete-node/ &removed_mem; ++/delete-node/ &camera_mem; ++/delete-node/ &wlan_mem; ++/delete-node/ &ipa_fw_mem; ++/delete-node/ &ipa_gsi_mem; ++/delete-node/ &gpu_mem; ++/delete-node/ &npu_mem; ++/delete-node/ &video_mem; ++/delete-node/ &cvp_mem; ++/delete-node/ &cdsp_mem; ++/delete-node/ &slpi_mem; ++/delete-node/ &adsp_mem; ++/delete-node/ &spss_mem; ++/delete-node/ &cdsp_secure_heap; ++ ++ ++/ { ++ aliases { ++ serial0 = &uart12; ++ serial1 = &uart6; ++ }; ++ ++ chosen { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ stdout-path = "serial0:115200n8"; ++ ++ framebuffer: framebuffer@9c000000 { ++ compatible = "simple-framebuffer"; ++ reg = <0 0x9c000000 0 0x2300000>; ++ width = <1080>; ++ height = <2400>; ++ stride = <(1080 * 4)>; ++ format = "a8r8g8b8"; ++ }; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ autorepeat; ++ ++ key-vol-up { ++ label = "Volume up"; ++ linux,code = ; ++ gpios = <&pm8150_gpios 6 GPIO_ACTIVE_LOW>; ++ }; ++ ++ key-vol-dowm { ++ label = "Volume down"; ++ linux,code = ; ++ gpios = <&pm8150_gpios 7 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ reserved-memory { ++ camera_mem: camera@8dc00000 { ++ reg = <0x0 0x8dc00000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ wlan_mem: wlan@8e100000 { ++ reg = <0x0 0x8e100000 0x0 0x100000>; ++ no-map; ++ }; ++ ++ ipa_fw_mem: ipa-fw@8e200000 { ++ reg = <0x0 0x8e200000 0x0 0x10000>; ++ no-map; ++ }; ++ ++ ipa_gsi_mem: ipa-gsi@8e210000 { ++ reg = <0x0 0x8e210000 0x0 0xa000>; ++ no-map; ++ }; ++ ++ gpu_mem: gpu@8e21a000 { ++ reg = <0x0 0x8e21a000 0x0 0x2000>; ++ no-map; ++ }; ++ ++ npu_mem: memory@8e300000 { ++ reg = <0x0 0x8e300000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ video_mem: video@8e800000 { ++ reg = <0x0 0x8e800000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ cvp_mem: cvp@8ed00000 { ++ reg = <0x0 0x8ed00000 0x0 0x500000>; ++ no-map; ++ }; ++ ++ cdsp_mem: cdsp@8f200000 { ++ reg = <0x0 0x8f200000 0x0 0x1400000>; ++ no-map; ++ }; ++ ++ slpi_mem: slpi@90600000 { ++ reg = <0x0 0x90600000 0x0 0x1500000>; ++ no-map; ++ }; ++ ++ adsp_mem: adsp@91b00000 { ++ reg = <0x00 0x91b00000 0x00 0x2500000>; ++ no-map; ++ }; ++ ++ spss_mem: spss@94000000 { ++ reg = <0x0 0x94000000 0x0 0x100000>; ++ no-map; ++ }; ++ ++ cdsp_secure_heap: cdsp-secure@94100000 { ++ reg = <0x0 0x94100000 0x0 0x4600000>; ++ no-map; ++ }; ++ ++ framebuffer@9c000000 { ++ reg = <0 0x9c000000 0 0x2300000>; ++ no-map; ++ }; ++ }; ++ ++ panel_avdd_5p5: regulator-panel-avdd { ++ compatible = "regulator-fixed"; ++ regulator-name = "panel_avdd_5p5"; ++ regulator-min-microvolt = <5500000>; ++ regulator-max-microvolt = <5500000>; ++ regulator-enable-ramp-delay = <233>; ++ gpio = <&tlmm 61 GPIO_ACTIVE_HIGH>; ++ regulator-boot-on; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&panel_avdd_pins>; ++ }; ++ ++ vph_pwr: regulator-vph-pwr { ++ compatible = "regulator-fixed"; ++ regulator-name = "vph_pwr"; ++ regulator-min-microvolt = <3700000>; ++ regulator-max-microvolt = <3700000>; ++ regulator-always-on; ++ }; ++ ++ vreg_s4a_1p8: regulator-vreg-s4a-1p8 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vreg_s4a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ }; ++ ++ qca6390-pmu { ++ compatible = "qcom,qca6390-pmu"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&bt_en_state>, <&wlan_en_state>; ++ ++ vddaon-supply = <&vreg_s6a_0p95>; ++ vddpmu-supply = <&vreg_s6a_0p95>; ++ vddrfa0p95-supply = <&vreg_s6a_0p95>; ++ vddrfa1p3-supply = <&vreg_s8c_1p3>; ++ vddrfa1p9-supply = <&vreg_s5a_1p9>; ++ vddpcie1p3-supply = <&vreg_s8c_1p3>; ++ vddpcie1p9-supply = <&vreg_s5a_1p9>; ++ vddio-supply = <&vreg_s4a_1p8>; ++ ++ wlan-enable-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; ++ bt-enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; ++ ++ regulators { ++ vreg_pmu_rfa_cmn: ldo0 { ++ regulator-name = "vreg_pmu_rfa_cmn"; ++ }; ++ ++ vreg_pmu_aon_0p59: ldo1 { ++ regulator-name = "vreg_pmu_aon_0p59"; ++ }; ++ ++ vreg_pmu_wlcx_0p8: ldo2 { ++ regulator-name = "vreg_pmu_wlcx_0p8"; ++ }; ++ ++ vreg_pmu_wlmx_0p85: ldo3 { ++ regulator-name = "vreg_pmu_wlmx_0p85"; ++ }; ++ ++ vreg_pmu_btcmx_0p85: ldo4 { ++ regulator-name = "vreg_pmu_btcmx_0p85"; ++ }; ++ ++ vreg_pmu_rfa_0p8: ldo5 { ++ regulator-name = "vreg_pmu_rfa_0p8"; ++ }; ++ ++ vreg_pmu_rfa_1p2: ldo6 { ++ regulator-name = "vreg_pmu_rfa_1p2"; ++ }; ++ ++ vreg_pmu_rfa_1p7: ldo7 { ++ regulator-name = "vreg_pmu_rfa_1p7"; ++ }; ++ ++ vreg_pmu_pcie_0p9: ldo8 { ++ regulator-name = "vreg_pmu_pcie_0p9"; ++ }; ++ ++ vreg_pmu_pcie_1p8: ldo9 { ++ regulator-name = "vreg_pmu_pcie_1p8"; ++ }; ++ }; ++ }; ++}; ++ ++&adsp { ++ firmware-name = "qcom/sm8250/OnePlus/adsp.mbn"; ++ status = "okay"; ++}; ++ ++&apps_rsc { ++ regulators-0 { ++ compatible = "qcom,pm8150-rpmh-regulators"; ++ qcom,pmic-id = "a"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-s3-supply = <&vph_pwr>; ++ vdd-s4-supply = <&vph_pwr>; ++ vdd-s5-supply = <&vph_pwr>; ++ vdd-s6-supply = <&vph_pwr>; ++ vdd-s7-supply = <&vph_pwr>; ++ vdd-s8-supply = <&vph_pwr>; ++ vdd-s9-supply = <&vph_pwr>; ++ vdd-s10-supply = <&vph_pwr>; ++ vdd-l2-l10-supply = <&vreg_bob>; ++ vdd-l3-l4-l5-l18-supply = <&vreg_s6a_0p95>; ++ vdd-l6-l9-supply = <&vreg_s8c_1p3>; ++ vdd-l7-l12-l14-l15-supply = <&vreg_s5a_1p9>; ++ vdd-l13-l16-l17-supply = <&vreg_bob>; ++ ++ vreg_s5a_1p9: smps5 { ++ regulator-name = "vreg_s5a_1p9"; ++ regulator-min-microvolt = <1904000>; ++ regulator-max-microvolt = <2000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_s6a_0p95: smps6 { ++ regulator-name = "vreg_s6a_0p95"; ++ regulator-min-microvolt = <920000>; ++ regulator-max-microvolt = <1128000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l2a_3p1: ldo2 { ++ regulator-name = "vreg_l2a_3p1"; ++ regulator-min-microvolt = <3072000>; ++ regulator-max-microvolt = <3072000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l3a_0p9: ldo3 { ++ regulator-name = "vreg_l3a_0p9"; ++ regulator-min-microvolt = <928000>; ++ regulator-max-microvolt = <932000>; ++ regulator-initial-mode = ; ++ }; ++ ++ /* l4 = VDD_SSC_MX (lmx.lvl) */ ++ ++ vreg_l5a_0p88: ldo5 { ++ regulator-name = "vreg_l5a_0p88"; ++ regulator-min-microvolt = <880000>; ++ regulator-max-microvolt = <880000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6a_1p2: ldo6 { ++ regulator-name = "vreg_l6a_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ regulator-allow-set-load; ++ regulator-allowed-modes = ; ++ }; ++ ++ vreg_l7a_1p7: ldo7 { ++ regulator-name = "vreg_l7a_1p7"; ++ regulator-min-microvolt = <1704000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l9a_1p2: ldo9 { ++ regulator-name = "vreg_l9a_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l10a_1p8: ldo10 { ++ regulator-name = "vreg_l10a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ /* l11 = VDD_SSC_CX (lxc.lvl) */ ++ ++ vreg_l12a_1p8: ldo12 { ++ regulator-name = "vreg_l12a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l13a_ts_3p0: ldo13 { ++ regulator-name = "vreg_l13a_ts_3p0"; ++ regulator-min-microvolt = <3008000>; ++ regulator-max-microvolt = <3008000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l14a_1p8: ldo14 { ++ regulator-name = "vreg_l14a_1p8"; ++ regulator-min-microvolt = <1650000>; ++ regulator-max-microvolt = <1880000>; ++ regulator-initial-mode = ; ++ regulator-boot-on; ++ }; ++ ++ vreg_l15a_1p8: ldo15 { ++ regulator-name = "vreg_l15a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ /* Fingerprint reader power (goodix_fp) */ ++ vreg_l16a_2p7: ldo16 { ++ regulator-name = "vreg_l16a_2p7"; ++ regulator-min-microvolt = <3024000>; ++ regulator-max-microvolt = <3304000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l17a_3p0: ldo17 { ++ regulator-name = "vreg_l17a_3p0"; ++ regulator-min-microvolt = <2504000>; ++ regulator-max-microvolt = <2950000>; ++ regulator-initial-mode = ; ++ regulator-allow-set-load; ++ regulator-allowed-modes = ; ++ }; ++ ++ vreg_l18a_0p92: ldo18 { ++ regulator-name = "vreg_l18a_0p92"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <920000>; ++ regulator-initial-mode = ; ++ }; ++ }; ++ ++ regulators-1 { ++ compatible = "qcom,pm8150l-rpmh-regulators"; ++ qcom,pmic-id = "c"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-s3-supply = <&vph_pwr>; ++ vdd-s4-supply = <&vph_pwr>; ++ vdd-s5-supply = <&vph_pwr>; ++ vdd-s6-supply = <&vph_pwr>; ++ vdd-s7-supply = <&vph_pwr>; ++ vdd-s8-supply = <&vph_pwr>; ++ vdd-l1-l8-supply = <&vreg_s4a_1p8>; ++ vdd-l2-l3-supply = <&vreg_s8c_1p3>; ++ vdd-l4-l5-l6-supply = <&vreg_bob>; ++ vdd-l7-l11-supply = <&vreg_bob>; ++ vdd-l9-l10-supply = <&vreg_bob>; ++ vdd-bob-supply = <&vph_pwr>; ++ ++ vreg_s8c_1p3: smps8 { ++ regulator-name = "vreg_s8c_1p3"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1400000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_bob: bob { ++ regulator-name = "vreg_bob"; ++ regulator-min-microvolt = <3008000>; ++ regulator-max-microvolt = <4000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l1c_1p8: ldo1 { ++ regulator-name = "vreg_l1c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l2c_1p2: ldo2 { ++ regulator-name = "vreg_l2c_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1304000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l3c_0p8: ldo3 { ++ regulator-name = "vreg_l3c_0p8"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l4c_1p7: ldo4 { ++ regulator-name = "vreg_l4c_1p7"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l5c_1p8: ldo5 { ++ regulator-name = "vreg_l5c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6c_2p96: ldo6 { ++ regulator-name = "vreg_l6c_2p96"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l7c_cam_vcm0_2p85: ldo7 { ++ regulator-name = "vreg_l7c_cam_vcm0_2p85"; ++ regulator-min-microvolt = <2856000>; ++ regulator-max-microvolt = <3104000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l8c_1p8: ldo8 { ++ regulator-name = "vreg_l8c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l9c_2p96: ldo9 { ++ regulator-name = "vreg_l9c_2p96"; ++ regulator-min-microvolt = <2704000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l10c_3p0: ldo10 { ++ regulator-name = "vreg_l10c_3p0"; ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3312000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l11c_3p3: ldo11 { ++ regulator-name = "vreg_l11c_3p3"; ++ regulator-min-microvolt = <2900000>; ++ regulator-max-microvolt = <3304000>; ++ regulator-initial-mode = ; ++ regulator-boot-on; ++ /* FIXME: we don't yet support power cycling the panel */ ++ //regulator-always-on; ++ }; ++ }; ++}; ++ ++&cdsp { ++ firmware-name = "qcom/sm8250/OnePlus/cdsp.mbn"; ++ status = "okay"; ++}; ++ ++&crypto { ++ status = "disabled"; ++}; ++ ++&gmu { ++ status = "okay"; ++}; ++ ++&gpu { ++ status = "okay"; ++ ++ zap-shader { ++ memory-region = <&gpu_mem>; ++ firmware-name = "qcom/sm8250/OnePlus/a650_zap.mbn"; ++ }; ++}; ++ ++&i2c2 { ++ status = "okay"; ++ ++ /* ++ * Pixelworks Iris 5 @ 26 (i3c) or 22 (i2c) ++ * This is a co-processor for the display which needs to be ++ * initialized along with the panel. ++ */ ++}; ++ ++&i2c15 { ++ status = "okay"; ++ ++ typec-mux@42 { ++ compatible = "fcs,fsa4480"; ++ reg = <0x42>; ++ ++ vcc-supply = <&vreg_s4a_1p8>; ++ ++ orientation-switch; ++ ++ /* Currently unsupported */ ++ status = "disabled"; ++ ++ port { ++ fsa4480_sbu_mux: endpoint { ++ }; ++ }; ++ }; ++}; ++ ++&i2c16 { ++ status = "okay"; ++ ++ bq27541_fg: bq27541-battery@55 { ++ compatible = "ti,bq27541"; ++ status = "okay"; ++ reg = <0x55>; ++ }; ++}; ++ ++&mdss { ++ status = "okay"; ++}; ++ ++&mdss_dsi0 { ++ vdda-supply = <&vreg_l9a_1p2>; ++ status = "okay"; ++ ++ display_panel: panel@0 { ++ reg = <0>; ++ vddio-supply = <&vreg_l14a_1p8>; ++ vdd-supply = <&vreg_l11c_3p3>; ++ avdd-supply = <&panel_avdd_5p5>; ++ /* ++ * FIXME: There is a bug somewhere in the display stack and it isn't ++ * possible to get the panel to a working state after toggling reset. ++ * At best it just shows one or more vertical red lines. So for now ++ * let's skip the reset GPIO. ++ */ ++ // reset-gpios = <&tlmm 75 GPIO_ACTIVE_LOW>; ++ ++ pinctrl-0 = <&panel_reset_pins &panel_vsync_pins &panel_vout_pins>; ++ pinctrl-names = "default"; ++ ++ status = "disabled"; ++ ++ port { ++ panel_in_0: endpoint { ++ remote-endpoint = <&mdss_dsi0_out>; ++ }; ++ }; ++ }; ++ ++}; ++ ++&mdss_dsi0_out { ++ data-lanes = <0 1 2 3>; ++ remote-endpoint = <&panel_in_0>; ++}; ++ ++&mdss_dsi0_phy { ++ vdds-supply = <&vreg_l5a_0p88>; ++ status = "okay"; ++}; ++ ++&pcie0 { ++ status = "okay"; ++}; ++ ++&pcie0_phy { ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++ ++ status = "okay"; ++}; ++ ++&pcieport0 { ++ wifi@0 { ++ compatible = "pci17cb,1101"; ++ reg = <0x10000 0x0 0x0 0x0 0x0>; ++ ++ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; ++ vddaon-supply = <&vreg_pmu_aon_0p59>; ++ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; ++ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; ++ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; ++ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; ++ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; ++ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; ++ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; ++ }; ++}; ++ ++&pcie2 { ++ status = "okay"; ++}; ++ ++&pcie2_phy { ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++ status = "okay"; ++}; ++ ++&pm8150_adc { ++ channel@4c { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "xo_therm"; ++ }; ++ ++ channel@4d { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "skin_therm"; ++ }; ++ ++ channel@4e { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "wifi_therm"; ++ }; ++}; ++ ++&pm8150_adc_tm { ++ status = "okay"; ++ ++ xo-therm@0 { ++ reg = <0>; ++ io-channels = <&pm8150_adc ADC5_XO_THERM_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++ ++ skin-therm@1 { ++ reg = <1>; ++ io-channels = <&pm8150_adc ADC5_XO_THERM_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++ ++ wifi-therm@2 { ++ reg = <2>; ++ io-channels = <&pm8150_adc ADC5_AMUX_THM2_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++}; ++ ++&pm8150_gpios { ++ /* ++ * These are marked as reserved in downstream ++ * with no description, without schematics we ++ * don't know what the deal is here. ++ */ ++ gpio-reserved-ranges = <2 1>, <4 2>, <8 1>; ++}; ++ ++&pm8150b_adc { ++ channel@4f { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "conn_therm"; ++ }; ++}; ++ ++&pm8150b_adc_tm { ++ status = "okay"; ++ ++ conn-therm@0 { ++ reg = <0>; ++ io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++}; ++ ++&pm8150l_adc { ++ channel@4e { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "skin_msm_therm"; ++ }; ++ ++ channel@4f { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "pm8150l_therm"; ++ }; ++}; ++ ++&pm8150l_adc_tm { ++ status = "okay"; ++ ++ skin-msm-therm@0 { ++ reg = <0>; ++ io-channels = <&pm8150l_adc ADC5_AMUX_THM2_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++ ++ pm8150l-therm@1 { ++ reg = <1>; ++ io-channels = <&pm8150l_adc ADC5_AMUX_THM3_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++}; ++ ++&pon_pwrkey { ++ status = "okay"; ++}; ++ ++&pon_resin { ++ status = "okay"; ++ ++ linux,code = ; ++}; ++ ++&qupv3_id_0 { ++ status = "okay"; ++}; ++ ++&qupv3_id_1 { ++ status = "okay"; ++}; ++ ++&qupv3_id_2 { ++ status = "okay"; ++}; ++ ++&tlmm { ++ gpio-reserved-ranges = <28 4>, <40 4>; ++ ++ bt_en_state: bt-default-state { ++ pins = "gpio21"; ++ function = "gpio"; ++ drive-strength = <16>; ++ output-low; ++ bias-pull-up; ++ }; ++ ++ wlan_en_state: wlan-default-state { ++ pins = "gpio20"; ++ function = "gpio"; ++ ++ drive-strength = <16>; ++ output-low; ++ bias-pull-up; ++ }; ++ ++ panel_reset_pins: panel-reset-state { ++ pins = "gpio75"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ ++ panel_vsync_pins: panel-vsync-state { ++ pins = "gpio66"; ++ function = "mdp_vsync"; ++ drive-strength = <16>; ++ bias-pull-down; ++ }; ++ ++ panel_vout_pins: panel-vout-state { ++ pins = "gpio24"; ++ function = "gpio"; ++ drive-strength = <16>; ++ output-high; ++ }; ++ ++ panel_avdd_pins: panel-avdd-state { ++ pins = "gpio65"; ++ function = "gpio"; ++ drive-strength = <8>; ++ output-high; ++ }; ++ ++ touch_irq_active: touch-irq-state { ++ pins = "gpio39"; ++ function = "gpio"; ++ drive-strength = <2>; ++ bias-pull-up; ++ output-disable; ++ }; ++ ++ touch_rst_active: touch-rst-state { ++ pins = "gpio38"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-pull-up; ++ }; ++}; ++ ++&uart6 { ++ status = "okay"; ++ ++ bluetooth { ++ compatible = "qcom,qca6390-bt"; ++ ++ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; ++ vddaon-supply = <&vreg_pmu_aon_0p59>; ++ vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; ++ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; ++ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; ++ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; ++ }; ++}; ++ ++&uart12 { ++ status = "okay"; ++}; ++ ++&ufs_mem_hc { ++ status = "okay"; ++ ++ vcc-supply = <&vreg_l17a_3p0>; ++ vcc-max-microamp = <800000>; ++ vccq-supply = <&vreg_l6a_1p2>; ++ vccq-max-microamp = <800000>; ++ vccq2-supply = <&vreg_s4a_1p8>; ++ vccq2-max-microamp = <800000>; ++}; ++ ++&ufs_mem_phy { ++ status = "okay"; ++ ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++}; ++ ++&usb_1 { ++ status = "okay"; ++}; ++ ++&usb_1_dwc3 { ++ dr_mode = "peripheral"; ++}; ++ ++&usb_1_hsphy { ++ status = "okay"; ++ ++ vdda-pll-supply = <&vreg_l5a_0p88>; ++ vdda33-supply = <&vreg_l2a_3p1>; ++ vdda18-supply = <&vreg_l12a_1p8>; ++}; ++ ++&usb_1_qmpphy { ++ status = "okay"; ++ ++ vdda-phy-supply = <&vreg_l9a_1p2>; ++ vdda-pll-supply = <&vreg_l18a_0p92>; ++}; ++ ++&venus { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts b/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (c) 2024, Caleb Connolly ++ */ ++ ++#include "sm8250-oneplus-common.dtsi" ++ ++/ { ++ model = "OnePlus 8T"; ++ compatible = "oneplus,kebab", "qcom,sm8250"; ++}; ++ ++&display_panel { ++ compatible = "samsung,amb655x"; ++ status = "okay"; ++}; ++ ++&i2c13 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ touchscreen@4b { ++ compatible = "syna,s3908"; ++ reg = <0x4B>; ++ ++ pinctrl-0 = <&touch_irq_active &touch_rst_active>; ++ pinctrl-names = "default"; ++ ++ interrupts-extended = <&tlmm 39 0x2008>; ++ ++ reset-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; ++ ++ vdd-supply = <&vreg_l13a_ts_3p0>; ++ vcc-supply = <&vreg_l1c_1p8>; ++ }; ++}; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0038-Input-driver-for-AYN-Odin2-Gamepad.patch b/patch/kernel/archive/sm8250-6.18/0038-Input-driver-for-AYN-Odin2-Gamepad.patch new file mode 100644 index 000000000000..c839b1e85b0f --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0038-Input-driver-for-AYN-Odin2-Gamepad.patch @@ -0,0 +1,362 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:13:18 +0700 +Subject: Input: driver for AYN Odin2 Gamepad + +There have been improvements from the original Odin 2 Gamepad driver +Signed-off-by: Teguh Sobirin +--- + drivers/input/joystick/Kconfig | 4 + + drivers/input/joystick/Makefile | 1 + + drivers/input/joystick/odin2.c | 313 ++++++++++ + 3 files changed, 318 insertions(+) + +diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig +index 111111111111..222222222222 100644 +--- a/drivers/input/joystick/Kconfig ++++ b/drivers/input/joystick/Kconfig +@@ -344,6 +344,10 @@ config JOYSTICK_MAPLE + To compile this as a module choose M here: the module will be called + maplecontrol. + ++config JOYSTICK_ODIN2 ++ tristate "AYN Odin2 gamepad" ++ depends on SERIAL_DEV_BUS ++ + config JOYSTICK_PSXPAD_SPI + tristate "PlayStation 1/2 joypads via SPI interface" + depends on SPI +diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/input/joystick/Makefile ++++ b/drivers/input/joystick/Makefile +@@ -25,6 +25,7 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o + obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o + obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o + obj-$(CONFIG_JOYSTICK_N64) += n64joy.o ++obj-$(CONFIG_JOYSTICK_ODIN2) += odin2.o + obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o + obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o + obj-$(CONFIG_JOYSTICK_QWIIC) += qwiic-joystick.o +diff --git a/drivers/input/joystick/odin2.c b/drivers/input/joystick/odin2.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/joystick/odin2.c +@@ -0,0 +1,313 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Driver for Ayn Odin 2 Gamepad ++ * ++ * Copyright (C) 2024 Molly Sophia ++ * Copyright (C) 2024 BigfootACA ++ * Copyright (C) 2024 Teguh Sobirin ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "odin2-gamepad" ++#define MAX_DATA_LEN 64 ++ ++#define gamepad_serdev_write_seq(serdev, seq...) \ ++ do { \ ++ static const u8 d[] = { seq }; \ ++ struct device *dev = &serdev->dev; \ ++ int ret; \ ++ ret = serdev_device_write_buf(serdev, d, ARRAY_SIZE(d)); \ ++ if (ret < 0 || ret < ARRAY_SIZE(d)) { \ ++ dev_warn_ratelimited(dev, "Unable to write data: %d", \ ++ ret); \ ++ return; \ ++ } \ ++ } while (0) ++ ++static const unsigned int keymap[] = { ++ BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, ++ BTN_NORTH, BTN_WEST, BTN_EAST, BTN_SOUTH, ++ BTN_TL, BTN_TR, BTN_SELECT, BTN_START, ++ BTN_THUMBL, BTN_THUMBR, BTN_MODE, BTN_BACK ++}; ++ ++struct gamepad_data { ++ u8 header[4]; ++ u8 frame_number; ++ u8 command; ++ u16 data_len; ++ u8 data[MAX_DATA_LEN]; ++}; ++ ++struct gamepad_device { ++ struct device *dev; ++ struct gpio_desc *boot_gpio; ++ struct gpio_desc *enable_gpio; ++ struct gpio_desc *reset_gpio; ++ struct input_dev *dev_input; ++}; ++ ++static u8 gamepad_data_checksum(const u8 *data, size_t count) ++{ ++ const u8 *ptr = data; ++ u8 ret = data[4]; ++ ++ for (int i = 5; i < count - 1; i++) ++ ret ^= ptr[i]; ++ ++ return ret; ++} ++ ++static void gamepad_send_init_sequence(struct serdev_device *serdev) ++{ ++ gamepad_serdev_write_seq(serdev, 0xA5, 0xD3, 0x5A, 0x3D, 0x0, 0x1, 0x2, ++ 0x0, 0x7, 0x1, 0x5); ++ msleep(100); ++ gamepad_serdev_write_seq(serdev, 0xA5, 0xD3, 0x5A, 0x3D, 0x1, 0x1, 0x1, ++ 0x0, 0x6, 0x7); ++ msleep(100); ++ gamepad_serdev_write_seq(serdev, 0xA5, 0xD3, 0x5A, 0x3D, 0x2, 0x1, 0x1, 0x0, 0x2, 0x0); ++ msleep(100); ++ gamepad_serdev_write_seq(serdev, 0xa5, 0xd3, 0x5a, 0x3d, 0x3, 0x01, ++ 0x0a, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x28, ++ 0x00, 0x00, 0x00, 0x07, 0x23); ++ msleep(100); ++ gamepad_serdev_write_seq(serdev, 0xA5, 0xD3, 0x5A, 0x3D, 0x4, 0x1, 0x1, ++ 0x0, 0x6, 0x2); ++ msleep(100); ++ gamepad_serdev_write_seq(serdev, 0xA5, 0xD3, 0x5A, 0x3D, 0x5, 0x1, 0x1, ++ 0x0, 0x2, 0x7); ++ msleep(100); ++} ++ ++static void gamepad_input_handler(struct gamepad_device *dev, ++ struct gamepad_data *data) ++{ ++ static unsigned long prev_states; ++ unsigned long keys = data->data[0] | (data->data[1] << 8); ++ unsigned long current_states = keys, changes; ++ int i; ++ struct input_dev *indev; ++ ++ indev = dev->dev_input; ++ if (!indev) ++ return; ++ ++ bitmap_xor(&changes, ¤t_states, &prev_states, ARRAY_SIZE(keymap)); ++ ++ for_each_set_bit(i, &changes, ARRAY_SIZE(keymap)) { ++ input_report_key(indev, keymap[i], (current_states & BIT(i))); ++ } ++ ++ input_report_abs(indev, ABS_HAT2X, ++ 0x755 - (data->data[2] | (data->data[3] << 8))); ++ input_report_abs(indev, ABS_HAT2Y, ++ 0x755 - (data->data[4] | (data->data[5] << 8))); ++ input_report_abs(indev, ABS_X, ++ -(int16_t)(data->data[6] | (data->data[7] << 8))); ++ input_report_abs(indev, ABS_Y, ++ -(int16_t)(data->data[8] | (data->data[9] << 8))); ++ input_report_abs(indev, ABS_RX, ++ -(int16_t)(data->data[10] | (data->data[11] << 8))); ++ input_report_abs(indev, ABS_RY, ++ -(int16_t)(data->data[12] | (data->data[13] << 8))); ++ ++ input_sync(indev); ++ prev_states = keys; ++} ++ ++static void gamepad_data_handler(struct serdev_device *serdev, ++ struct gamepad_data *data) ++{ ++ struct gamepad_device *dev = serdev_device_get_drvdata(serdev); ++ ++ switch (data->command) { ++ case 0x2: ++ gamepad_input_handler(dev, data); ++ break; ++ default: ++ break; ++ } ++} ++ ++static size_t gamepad_mcu_uart_rx_bytes(struct serdev_device *serdev, ++ const u8 *data, size_t count) ++{ ++ struct device *dev = &serdev->dev; ++ struct gamepad_data recv_data_buffer; ++ u8 checksum; ++ ++ if (!data || count < 7) { ++ dev_warn_ratelimited(dev, "invalid packet"); ++ return count; ++ } ++ ++ checksum = gamepad_data_checksum(data, count); ++ if (checksum != *(data + count - 1)) { ++ dev_warn_ratelimited(dev, "packet checksum failed"); ++ return count; ++ } ++ ++ memcpy(recv_data_buffer.header, data, 4); ++ recv_data_buffer.frame_number = *(data + 4); ++ recv_data_buffer.command = *(data + 5); ++ recv_data_buffer.data_len = *(data + 6) | (*(data + 7) << 8); ++ ++ if (recv_data_buffer.data_len) { ++ memset(&recv_data_buffer.data[0], 0, ++ sizeof(recv_data_buffer.data)); ++ memcpy(&recv_data_buffer.data[0], data + 8, ++ recv_data_buffer.data_len); ++ } ++ ++ gamepad_data_handler(serdev, &recv_data_buffer); ++ ++ return count; ++} ++ ++static const struct serdev_device_ops gamepad_mcu_uart_client_ops = { ++ .receive_buf = gamepad_mcu_uart_rx_bytes, ++}; ++ ++static int gamepad_mcu_uart_probe(struct serdev_device *serdev) ++{ ++ struct device *dev = &serdev->dev; ++ struct gamepad_device *gamepad_dev; ++ u32 gamepad_bus = 0; ++ u32 gamepad_vid = 0; ++ u32 gamepad_pid = 0; ++ u32 gamepad_rev = 0; ++ int ret; ++ ++ gamepad_dev = devm_kzalloc(dev, sizeof(*gamepad_dev), GFP_KERNEL); ++ if (!gamepad_dev) ++ return dev_err_probe(dev, -ENOMEM, "could not allocate memory for device data\n"); ++ ++ gamepad_dev->boot_gpio = ++ devm_gpiod_get_optional(dev, "boot", GPIOD_OUT_HIGH); ++ if (IS_ERR(gamepad_dev->boot_gpio)) { ++ ret = PTR_ERR(gamepad_dev->boot_gpio); ++ dev_warn(dev, "Unable to get boot gpio: %d\n", ret); ++ } ++ ++ gamepad_dev->enable_gpio = ++ devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); ++ if (IS_ERR(gamepad_dev->enable_gpio)) { ++ ret = PTR_ERR(gamepad_dev->enable_gpio); ++ dev_warn(dev, "Unable to get enable gpio: %d\n", ret); ++ } ++ ++ gamepad_dev->reset_gpio = ++ devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(gamepad_dev->reset_gpio)) { ++ ret = PTR_ERR(gamepad_dev->reset_gpio); ++ dev_warn(dev, "Unable to get reset gpio: %d\n", ret); ++ } ++ ++ if (gamepad_dev->boot_gpio) ++ gpiod_set_value_cansleep(gamepad_dev->boot_gpio, 0); ++ ++ if (gamepad_dev->reset_gpio) ++ gpiod_set_value_cansleep(gamepad_dev->reset_gpio, 0); ++ ++ msleep(100); ++ ++ if (gamepad_dev->enable_gpio) ++ gpiod_set_value_cansleep(gamepad_dev->enable_gpio, 1); ++ ++ if (gamepad_dev->reset_gpio) ++ gpiod_set_value_cansleep(gamepad_dev->reset_gpio, 1); ++ ++ msleep(100); ++ ++ ret = devm_serdev_device_open(dev, serdev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Unable to open UART device\n"); ++ gamepad_dev->dev = dev; ++ ++ serdev_device_set_drvdata(serdev, gamepad_dev); ++ ++ ret = serdev_device_set_baudrate(serdev, 115200); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Failed to set up host baud rate\n"); ++ ++ serdev_device_set_flow_control(serdev, false); ++ ++ gamepad_dev->dev_input = devm_input_allocate_device(dev); ++ if (!gamepad_dev->dev_input) ++ return dev_err_probe(dev, -ENOMEM, "Not enough memory for input device\n"); ++ ++ gamepad_dev->dev_input->phys = DRIVER_NAME"/input0"; ++ ++ ret = device_property_read_string(dev, "gamepad-name", &gamepad_dev->dev_input->name); ++ if (ret) { ++ gamepad_dev->dev_input->name = "Ayn Odin2 Gamepad"; ++ } ++ ++ device_property_read_u32(dev, "gamepad-bus", &gamepad_bus); ++ device_property_read_u32(dev, "gamepad-vid", &gamepad_vid); ++ device_property_read_u32(dev, "gamepad-pid", &gamepad_pid); ++ device_property_read_u32(dev, "gamepad-rev", &gamepad_rev); ++ ++ gamepad_dev->dev_input->id.bustype = (u16)gamepad_bus; ++ gamepad_dev->dev_input->id.vendor = (u16)gamepad_vid; ++ gamepad_dev->dev_input->id.product = (u16)gamepad_pid; ++ gamepad_dev->dev_input->id.version = (u16)gamepad_rev; ++ ++ __set_bit(EV_KEY, gamepad_dev->dev_input->evbit); ++ for (int i = 0; i < ARRAY_SIZE(keymap); i++) ++ input_set_capability(gamepad_dev->dev_input, EV_KEY, keymap[i]); ++ ++ __set_bit(EV_ABS, gamepad_dev->dev_input->evbit); ++ for (int i = ABS_X; i <= ABS_RZ; i++) ++ input_set_abs_params(gamepad_dev->dev_input, i, -0x580, 0x580, ++ 0, 0); ++ ++ input_set_abs_params(gamepad_dev->dev_input, ABS_HAT2X, 0, 1830, 0, 30); ++ input_set_abs_params(gamepad_dev->dev_input, ABS_HAT2Y, 0, 1830, 0, 30); ++ ++ ret = input_register_device(gamepad_dev->dev_input); ++ if (ret) ++ return dev_err_probe(dev, ret, "Could not register input device\n"); ++ ++ serdev_device_set_client_ops(serdev, &gamepad_mcu_uart_client_ops); ++ ++ gamepad_send_init_sequence(serdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id gamepad_mcu_uart_of_match[] = { ++ { .compatible = "ayn,odin2-gamepad" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, gamepad_mcu_uart_of_match); ++ ++static struct serdev_device_driver gamepad_mcu_uart_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .of_match_table = gamepad_mcu_uart_of_match, ++ }, ++ .probe = gamepad_mcu_uart_probe, ++}; ++ ++module_serdev_device_driver(gamepad_mcu_uart_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Gamepad driver for Ayn Odin2"); ++MODULE_AUTHOR("Molly Sophia "); ++MODULE_AUTHOR("BigfootACA "); ++MODULE_AUTHOR("Teguh Sobirin "); ++MODULE_ALIAS("platform:" DRIVER_NAME); +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0039-input-add-Qualcomm-SPMI-haptics-driver.patch b/patch/kernel/archive/sm8250-6.18/0039-input-add-Qualcomm-SPMI-haptics-driver.patch new file mode 100644 index 000000000000..2437d2d0979e --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0039-input-add-Qualcomm-SPMI-haptics-driver.patch @@ -0,0 +1,1090 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Mon, 25 Nov 2024 02:27:16 +0700 +Subject: input: add Qualcomm SPMI haptics driver + +Add support for the haptics found in pmi8998 and related PMICs. +Based on the ff-memless interface. Currently this driver provides +a partial implementation of hardware features. + +This driver only supports LRAs (Linear Resonant Actuators) in the "buffer" +mode with a single wave pattern. +Signed-off-by: Teguh Sobirin +--- + drivers/input/misc/Kconfig | 12 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/qcom-spmi-haptics.c | 990 ++++++++++ + include/dt-bindings/input/qcom,spmi-haptics.h | 31 + + 4 files changed, 1034 insertions(+) + +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 111111111111..222222222222 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -214,6 +214,18 @@ config INPUT_PMIC8XXX_PWRKEY + To compile this driver as a module, choose M here: the + module will be called pmic8xxx-pwrkey. + ++config INPUT_QCOM_SPMI_HAPTICS ++ tristate "Qualcomm SPMI HAPTICS" ++ depends on ARCH_QCOM ++ depends on SPMI ++ select INPUT_FF_MEMLESS ++ help ++ This option enables support for the haptics found in pmi8998 and ++ related PMICs. Based on the ff-memless interface. ++ ++ To compile this driver as module, choose M here: the ++ module will be called qcom_spmi_haptics. ++ + config INPUT_SPARCSPKR + tristate "SPARC Speaker support" + depends on PCI && SPARC64 +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -41,6 +41,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o + obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o + obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o + obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o ++obj-$(CONFIG_INPUT_QCOM_SPMI_HAPTICS) += qcom-spmi-haptics.o + obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o + obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o + obj-$(CONFIG_INPUT_IBM_PANEL) += ibm-panel.o +diff --git a/drivers/input/misc/qcom-spmi-haptics.c b/drivers/input/misc/qcom-spmi-haptics.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/input/misc/qcom-spmi-haptics.c +@@ -0,0 +1,990 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2021, Caleb Connolly ++ * Qualcomm QPMI haptics driver for pmi8998 and related PMICs. ++ */ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HAP_STATUS_1_REG 0x0A ++#define HAP_BUSY_BIT BIT(1) ++#define SC_FLAG_BIT BIT(3) ++#define AUTO_RES_ERROR_BIT BIT(4) ++ ++#define HAP_LRA_AUTO_RES_LO_REG 0x0B ++#define HAP_LRA_AUTO_RES_HI_REG 0x0C ++ ++#define HAP_EN_CTL_REG 0x46 ++#define HAP_EN_BIT BIT(7) ++ ++#define HAP_EN_CTL2_REG 0x48 ++#define BRAKE_EN_BIT BIT(0) ++ ++#define HAP_AUTO_RES_CTRL_REG 0x4B ++#define AUTO_RES_EN_BIT BIT(7) ++#define AUTO_RES_ERR_RECOVERY_BIT BIT(3) ++#define AUTO_RES_EN_FLAG_BIT BIT(0) ++ ++#define HAP_CFG1_REG 0x4C ++#define HAP_ACT_TYPE_MASK BIT(0) ++ ++#define HAP_CFG2_REG 0x4D ++#define HAP_LRA_RES_TYPE_MASK BIT(0) ++ ++#define HAP_SEL_REG 0x4E ++#define HAP_WF_SOURCE_MASK GENMASK(5, 4) ++#define HAP_WF_SOURCE_SHIFT 4 ++ ++#define HAP_LRA_AUTO_RES_REG 0x4F ++#define LRA_AUTO_RES_MODE_MASK GENMASK(6, 4) ++#define LRA_AUTO_RES_MODE_SHIFT 4 ++#define LRA_HIGH_Z_MASK GENMASK(3, 2) ++#define LRA_HIGH_Z_SHIFT 2 ++#define LRA_RES_CAL_MASK GENMASK(1, 0) ++#define HAP_RES_CAL_PERIOD_MIN 4 ++#define HAP_RES_CAL_PERIOD_MAX 32 ++ ++#define HAP_VMAX_CFG_REG 0x51 ++#define HAP_VMAX_OVD_BIT BIT(6) ++#define HAP_VMAX_MASK GENMASK(5, 1) ++#define HAP_VMAX_SHIFT 1 ++ ++#define HAP_ILIM_CFG_REG 0x52 ++#define HAP_ILIM_SEL_MASK BIT(0) ++#define HAP_ILIM_400_MA 0 ++#define HAP_ILIM_800_MA 1 ++ ++#define HAP_SC_DEB_REG 0x53 ++#define HAP_SC_DEB_MASK GENMASK(2, 0) ++#define HAP_SC_DEB_CYCLES_MIN 0 ++#define HAP_DEF_SC_DEB_CYCLES 8 ++#define HAP_SC_DEB_CYCLES_MAX 32 ++ ++#define HAP_RATE_CFG1_REG 0x54 ++#define HAP_RATE_CFG1_MASK GENMASK(7, 0) ++#define HAP_RATE_CFG2_SHIFT 8 // As CFG2 is the most significant byte ++ ++#define HAP_RATE_CFG2_REG 0x55 ++#define HAP_RATE_CFG2_MASK GENMASK(3, 0) ++ ++#define HAP_SC_CLR_REG 0x59 ++#define SC_CLR_BIT BIT(0) ++ ++#define HAP_BRAKE_REG 0x5C ++#define HAP_BRAKE_PAT_MASK 0x3 ++ ++#define HAP_WF_REPEAT_REG 0x5E ++#define WF_REPEAT_MASK GENMASK(6, 4) ++#define WF_REPEAT_SHIFT 4 ++#define WF_REPEAT_MIN 1 ++#define WF_REPEAT_MAX 128 ++#define WF_S_REPEAT_MASK GENMASK(1, 0) ++#define WF_S_REPEAT_MIN 1 ++#define WF_S_REPEAT_MAX 8 ++ ++#define HAP_WF_S1_REG 0x60 ++#define HAP_WF_SIGN_BIT BIT(7) ++#define HAP_WF_OVD_BIT BIT(6) ++#define HAP_WF_SAMP_MAX GENMASK(5, 1) ++#define HAP_WF_SAMPLE_LEN 8 ++ ++#define HAP_PLAY_REG 0x70 ++#define HAP_PLAY_BIT BIT(7) ++#define HAP_PAUSE_BIT BIT(0) ++ ++#define HAP_SEC_ACCESS_REG 0xD0 ++#define HAP_SEC_ACCESS_UNLOCK 0xA5 ++ ++#define HAP_TEST2_REG 0xE3 ++ ++ ++#define HAP_VMAX_MIN_MV 116 ++#define HAP_VMAX_MAX_MV 3596 ++#define HAP_VMAX_MAX_MV_STRONG 3596 ++ ++#define HAP_WAVE_PLAY_RATE_MIN_US 0 ++#define HAP_WAVE_PLAY_RATE_MAX_US 20475 ++#define HAP_WAVE_PLAY_TIME_MAX_MS 15000 ++ ++#define AUTO_RES_ERR_POLL_TIME_NS (20 * NSEC_PER_MSEC) ++#define HAPTICS_BACK_EMF_DELAY_US 20000 ++ ++#define HAP_BRAKE_PAT_LEN 4 ++#define HAP_WAVE_SAMP_LEN 8 ++#define NUM_WF_SET 4 ++#define HAP_WAVE_SAMP_SET_LEN (HAP_WAVE_SAMP_LEN * NUM_WF_SET) ++#define HAP_RATE_CFG_STEP_US 5 ++ ++#define SC_MAX_COUNT 5 ++#define SC_COUNT_RST_DELAY_US 1000000 ++ ++enum hap_play_control { ++ HAP_STOP, ++ HAP_PAUSE, ++ HAP_PLAY, ++}; ++ ++/** ++ * struct spmi_haptics - struct for spmi haptics data. ++ * ++ * @dev: Our device parent. ++ * @regmap: Register map for the hardware block. ++ * @input_dev: The input device used to receive events. ++ * @work: Work struct to play effects. ++ * @base: Base address of the regmap. ++ * @active: Atomic value used to track if haptics are currently playing. ++ * @play_irq: Fired to load the next wave pattern. ++ * @sc_irq: Short circuit irq. ++ * @last_sc_time: Time since the short circuit IRQ last fired. ++ * @sc_count: Number of times the short circuit IRQ has fired in this interval. ++ * @actuator_type: The type of actuator in use. ++ * @wave_shape: The shape of the waves to use (sine or square). ++ * @play_mode: The play mode to use (direct, buffer, pwm, audio). ++ * @magnitude: The strength we should be playing at. ++ * @vmax: Max voltage to use when playing. ++ * @current_limit: The current limit for this hardware (400mA or 800mA). ++ * @play_wave_rate: The wave rate to use for this hardware. ++ * @wave_samp: The array of wave samples to write for buffer mode. ++ * @brake_pat: The pattern to apply when braking. ++ * @play_lock: Lock to be held when updating the hardware state. ++ */ ++struct spmi_haptics { ++ struct device *dev; ++ struct regmap *regmap; ++ struct input_dev *haptics_input_dev; ++ struct gpio_desc *boost; ++ struct work_struct work; ++ u32 base; ++ ++ atomic_t active; ++ ++ int play_irq; ++ int sc_irq; ++ ktime_t last_sc_time; ++ u8 sc_count; ++ ++ u8 actuator_type; ++ u8 wave_shape; ++ u8 play_mode; ++ int magnitude; ++ u32 vmax; ++ u32 current_limit; ++ u32 play_wave_rate; ++ ++ u32 wave_samp[HAP_WAVE_SAMP_SET_LEN]; ++ u8 brake_pat[HAP_BRAKE_PAT_LEN]; ++ ++ struct mutex play_lock; ++}; ++ ++static inline bool is_secure_addr(u16 addr) ++{ ++ return (addr & 0xFF) > 0xD0; ++} ++ ++static int spmi_haptics_read(struct spmi_haptics *haptics, ++ u16 addr, u8 *val, int len) ++{ ++ int ret; ++ ++ ret = regmap_bulk_read(haptics->regmap, addr, val, len); ++ if (ret < 0) ++ dev_err(haptics->dev, "Error reading address: 0x%x, ret %d\n", addr, ret); ++ ++ return ret; ++} ++ ++static int spmi_haptics_write(struct spmi_haptics *haptics, ++ u16 addr, u8 *val, int len) ++{ ++ int ret, i; ++ ++ if (is_secure_addr(addr)) { ++ for (i = 0; i < len; i++) { ++ dev_dbg(haptics->dev, "%s: unlocking for addr: 0x%x, val: 0x%x", __func__, ++ addr, val[i]); ++ ret = regmap_write(haptics->regmap, ++ haptics->base + HAP_SEC_ACCESS_REG, HAP_SEC_ACCESS_UNLOCK); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error writing unlock code, ret %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = regmap_write(haptics->regmap, addr + i, val[i]); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error writing address 0x%x, ret %d\n", ++ addr + i, ret); ++ return ret; ++ } ++ } ++ } else { ++ if (len > 1) ++ ret = regmap_bulk_write(haptics->regmap, addr, val, len); ++ else ++ ret = regmap_write(haptics->regmap, addr, *val); ++ } ++ ++ if (ret < 0) ++ dev_err(haptics->dev, "%s: Error writing address: 0x%x, ret %d\n", ++ __func__, addr, ret); ++ ++ return ret; ++} ++ ++static int spmi_haptics_write_masked(struct spmi_haptics *haptics, ++ u16 addr, u8 mask, u8 val) ++{ ++ int ret; ++ ++ if (is_secure_addr(addr)) { ++ ret = regmap_write(haptics->regmap, ++ haptics->base + HAP_SEC_ACCESS_REG, HAP_SEC_ACCESS_UNLOCK); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error writing unlock code - ret %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ret = regmap_update_bits(haptics->regmap, addr, mask, val); ++ if (ret < 0) ++ dev_err(haptics->dev, "Error writing address: 0x%x - ret %d\n", addr, ret); ++ ++ return ret; ++} ++ ++static bool is_haptics_idle(struct spmi_haptics *haptics) ++{ ++ int ret; ++ u8 val; ++ ++ if (haptics->play_mode == HAP_PLAY_DIRECT || ++ haptics->play_mode == HAP_PLAY_PWM) ++ return true; ++ ++ ret = spmi_haptics_read(haptics, haptics->base + HAP_STATUS_1_REG, &val, 1); ++ if (ret < 0 || (val & HAP_BUSY_BIT)) ++ return false; ++ ++ return true; ++} ++ ++static int spmi_haptics_module_enable(struct spmi_haptics *haptics, bool enable) ++{ ++ u8 val; ++ ++ dev_dbg(haptics->dev, "Setting module enable: %d", enable); ++ ++ val = enable ? HAP_EN_BIT : 0; ++ return spmi_haptics_write(haptics, haptics->base + HAP_EN_CTL_REG, &val, 1); ++} ++ ++static int spmi_haptics_write_vmax(struct spmi_haptics *haptics) ++{ ++ u8 val = 0; ++ u32 vmax_mv = haptics->vmax; ++ ++ vmax_mv = clamp_t(u32, vmax_mv, HAP_VMAX_MIN_MV, HAP_VMAX_MAX_MV); ++ ++ dev_dbg(haptics->dev, "Setting vmax to: %d", vmax_mv); ++ ++ val = DIV_ROUND_CLOSEST(vmax_mv, HAP_VMAX_MIN_MV); ++ val = FIELD_PREP(HAP_VMAX_MASK, val); ++ ++ // TODO: pm660 can enable overdrive here ++ ++ return spmi_haptics_write_masked(haptics, haptics->base + HAP_VMAX_CFG_REG, ++ HAP_VMAX_MASK | HAP_WF_OVD_BIT, val); ++} ++ ++static int spmi_haptics_write_current_limit(struct spmi_haptics *haptics) ++{ ++ haptics->current_limit = clamp_t(u32, haptics->current_limit, ++ HAP_ILIM_400_MA, HAP_ILIM_800_MA); ++ ++ dev_dbg(haptics->dev, "Setting current_limit to: 0x%x", haptics->current_limit); ++ ++ return spmi_haptics_write_masked(haptics, haptics->base + HAP_ILIM_CFG_REG, ++ HAP_ILIM_SEL_MASK, haptics->current_limit); ++} ++ ++static int spmi_haptics_write_play_mode(struct spmi_haptics *haptics) ++{ ++ u8 val = 0; ++ ++ if (!is_haptics_idle(haptics)) ++ return -EBUSY; ++ ++ dev_dbg(haptics->dev, "Setting play_mode to: 0x%x", haptics->play_mode); ++ ++ val = FIELD_PREP(HAP_WF_SOURCE_MASK, haptics->play_mode); ++ return spmi_haptics_write_masked(haptics, haptics->base + HAP_SEL_REG, ++ HAP_WF_SOURCE_MASK, val); ++ ++} ++ ++static int spmi_haptics_write_play_rate(struct spmi_haptics *haptics, u16 play_rate) ++{ ++ u8 val[2]; ++ ++ dev_dbg(haptics->dev, "Setting play_rate to: %d", play_rate); ++ ++ val[0] = FIELD_PREP(HAP_RATE_CFG1_MASK, play_rate); ++ val[1] = FIELD_PREP(HAP_RATE_CFG2_MASK, play_rate >> HAP_RATE_CFG2_SHIFT); ++ return spmi_haptics_write(haptics, haptics->base + HAP_RATE_CFG1_REG, val, 2); ++} ++ ++/* ++ * spmi_haptics_set_auto_res() - Auto resonance ++ * allows the haptics to automatically adjust the ++ * speed of the oscillation in order to maintain ++ * the resonant frequency. ++ */ ++static int spmi_haptics_set_auto_res(struct spmi_haptics *haptics, bool enable) ++{ ++ u8 val; ++ ++ // LRAs are the only type to support auto res ++ if (haptics->actuator_type != HAP_TYPE_LRA) ++ return 0; ++ ++ val = enable ? AUTO_RES_EN_BIT : 0; ++ ++ return spmi_haptics_write_masked(haptics, haptics->base + HAP_TEST2_REG, ++ AUTO_RES_EN_BIT, val); ++} ++ ++static int spmi_haptics_write_brake(struct spmi_haptics *haptics) ++{ ++ int ret, i; ++ u8 val; ++ ++ dev_dbg(haptics->dev, "Configuring brake pattern"); ++ ++ ret = spmi_haptics_write_masked(haptics, haptics->base + HAP_EN_CTL2_REG, ++ BRAKE_EN_BIT, 1); ++ if (ret < 0) ++ return ret; ++ ++ for (i = HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { ++ u8 p = haptics->brake_pat[i] & HAP_BRAKE_PAT_MASK; ++ ++ val |= p << (i * 2); ++ } ++ ++ return spmi_haptics_write(haptics, haptics->base + HAP_BRAKE_REG, &val, 1); ++} ++ ++static int spmi_haptics_write_buffer_config(struct spmi_haptics *haptics) ++{ ++ u8 buf[HAP_WAVE_SAMP_LEN]; ++ int i; ++ ++ dev_dbg(haptics->dev, "Writing buffer config"); ++ ++ for (i = 0; i < HAP_WAVE_SAMP_LEN; i++) ++ buf[i] = haptics->wave_samp[i]; ++ ++ return spmi_haptics_write(haptics, haptics->base + HAP_WF_S1_REG, buf, ++ HAP_WAVE_SAMP_LEN); ++} ++ ++/* ++ * spmi_haptics_write_wave_repeat() - write wave repeat values. ++ */ ++static int spmi_haptics_write_wave_repeat(struct spmi_haptics *haptics) ++{ ++ u8 val, mask; ++ ++ /* The number of times to repeat each wave */ ++ mask = WF_REPEAT_MASK | WF_S_REPEAT_MASK; ++ val = FIELD_PREP(WF_REPEAT_MASK, 0) | ++ FIELD_PREP(WF_S_REPEAT_MASK, 0); ++ ++ return spmi_haptics_write_masked(haptics, haptics->base + HAP_WF_REPEAT_REG, ++ mask, val); ++} ++ ++static int spmi_haptics_write_play_control(struct spmi_haptics *haptics, ++ enum hap_play_control ctrl) ++{ ++ u8 val; ++ ++ switch (ctrl) { ++ case HAP_STOP: ++ val = 0; ++ break; ++ case HAP_PAUSE: ++ val = HAP_PAUSE_BIT; ++ break; ++ case HAP_PLAY: ++ val = HAP_PLAY_BIT; ++ break; ++ default: ++ return 0; ++ } ++ ++ dev_dbg(haptics->dev, "haptics play ctrl: %d\n", ctrl); ++ return spmi_haptics_write(haptics, haptics->base + HAP_PLAY_REG, &val, 1); ++} ++ ++/* ++ * This IRQ is fired to tell us to load the next wave sample set. ++ * As we only currently support a single sample set, it's unused. ++ */ ++static irqreturn_t spmi_haptics_play_irq_handler(int irq, void *data) ++{ ++ struct spmi_haptics *haptics = data; ++ ++ dev_dbg(haptics->dev, "play_irq triggered"); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Fires every ~50ms whilst the haptics are active. ++ * If the SC_FLAG_BIT is set then that means there isn't a short circuit ++ * and we just need to clear the IRQ to indicate that the device should ++ * keep vibrating. ++ * ++ * Otherwise, it means a short circuit situation has occurred. ++ */ ++static irqreturn_t spmi_haptics_sc_irq_handler(int irq, void *data) ++{ ++ struct spmi_haptics *haptics = data; ++ int ret; ++ u8 val; ++ s64 sc_delta_time_us; ++ ktime_t temp; ++ ++ ret = spmi_haptics_read(haptics, haptics->base + HAP_STATUS_1_REG, &val, 1); ++ if (ret < 0) ++ return IRQ_HANDLED; ++ ++ if (!(val & SC_FLAG_BIT)) { ++ haptics->sc_count = 0; ++ return IRQ_HANDLED; ++ } ++ ++ temp = ktime_get(); ++ sc_delta_time_us = ktime_us_delta(temp, haptics->last_sc_time); ++ haptics->last_sc_time = temp; ++ ++ if (sc_delta_time_us > SC_COUNT_RST_DELAY_US) ++ haptics->sc_count = 0; ++ else ++ haptics->sc_count++; ++ ++ // Clear the interrupt flag ++ val = SC_CLR_BIT; ++ ret = spmi_haptics_write(haptics, haptics->base + HAP_SC_CLR_REG, &val, 1); ++ if (ret < 0) ++ return IRQ_HANDLED; ++ ++ if (haptics->sc_count > SC_MAX_COUNT) { ++ dev_crit(haptics->dev, "Short circuit persists, disabling haptics\n"); ++ ret = spmi_haptics_module_enable(haptics, false); ++ if (ret < 0) ++ dev_err(haptics->dev, "Error disabling module, rc=%d\n", ret); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/** ++ * spmi_haptics_init() - Initialise haptics hardware for use ++ * @haptics: haptics device ++ * Returns: 0 on success, < 0 on error ++ */ ++static int spmi_haptics_init(struct spmi_haptics *haptics) ++{ ++ int ret; ++ u8 val, mask; ++ u16 play_rate; ++ ++ ret = spmi_haptics_write_masked(haptics, haptics->base + HAP_CFG1_REG, ++ HAP_ACT_TYPE_MASK, haptics->actuator_type); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * Configure auto resonance ++ * see spmi_haptics_lra_auto_res_config downstream ++ * This is greatly simplified. ++ */ ++ val = FIELD_PREP(LRA_RES_CAL_MASK, ilog2(32 / HAP_RES_CAL_PERIOD_MIN)) | ++ FIELD_PREP(LRA_AUTO_RES_MODE_MASK, HAP_AUTO_RES_ZXD_EOP) | ++ FIELD_PREP(LRA_HIGH_Z_MASK, 1); ++ ++ mask = LRA_AUTO_RES_MODE_MASK | LRA_HIGH_Z_MASK | LRA_RES_CAL_MASK; ++ ++ ret = spmi_haptics_write_masked(haptics, haptics->base + HAP_LRA_AUTO_RES_REG, ++ mask, val); ++ ++ /* Configure the PLAY MODE register */ ++ ret = spmi_haptics_write_play_mode(haptics); ++ if (ret < 0) ++ return ret; ++ ++ ret = spmi_haptics_write_vmax(haptics); ++ if (ret < 0) ++ return ret; ++ ++ /* Configure the ILIM register */ ++ ret = spmi_haptics_write_current_limit(haptics); ++ if (ret < 0) ++ return ret; ++ ++ // Configure the debounce for short-circuit detection. ++ ret = spmi_haptics_write_masked(haptics, haptics->base + HAP_SC_DEB_REG, ++ HAP_SC_DEB_MASK, HAP_SC_DEB_CYCLES_MAX); ++ if (ret < 0) ++ return ret; ++ ++ // write the wave shape ++ ret = spmi_haptics_write_masked(haptics, haptics->base + HAP_CFG2_REG, ++ HAP_LRA_RES_TYPE_MASK, haptics->wave_shape); ++ if (ret < 0) ++ return ret; ++ ++ play_rate = haptics->play_wave_rate / HAP_RATE_CFG_STEP_US; ++ ++ /* ++ * Configure RATE_CFG1 and RATE_CFG2 registers. ++ * Note: For ERM these registers act as play rate and ++ * for LRA these represent resonance period ++ */ ++ ret = spmi_haptics_write_play_rate(haptics, play_rate); ++ if (ret < 0) ++ return ret; ++ ++ ret = spmi_haptics_write_brake(haptics); ++ if (ret < 0) ++ return ret; ++ ++ if (haptics->play_mode == HAP_PLAY_BUFFER) { ++ ret = spmi_haptics_write_wave_repeat(haptics); ++ if (ret < 0) ++ return ret; ++ ++ ret = spmi_haptics_write_buffer_config(haptics); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (haptics->play_irq >= 0) { ++ dev_dbg(haptics->dev, "%s: Requesting play IRQ, irq: %d", __func__, ++ haptics->play_irq); ++ ret = devm_request_threaded_irq(haptics->dev, haptics->play_irq, ++ NULL, spmi_haptics_play_irq_handler, IRQF_ONESHOT, ++ "haptics_play_irq", haptics); ++ ++ if (ret < 0) { ++ dev_err(haptics->dev, "Unable to request play IRQ ret=%d\n", ret); ++ return ret; ++ } ++ ++ /* use play_irq only for buffer mode */ ++ if (haptics->play_mode != HAP_PLAY_BUFFER) ++ disable_irq(haptics->play_irq); ++ } ++ ++ if (haptics->sc_irq >= 0) { ++ dev_dbg(haptics->dev, "%s: Requesting play IRQ, irq: %d", __func__, ++ haptics->play_irq); ++ ret = devm_request_threaded_irq(haptics->dev, haptics->sc_irq, ++ NULL, spmi_haptics_sc_irq_handler, IRQF_ONESHOT, ++ "haptics_sc_irq", haptics); ++ ++ if (ret < 0) { ++ dev_err(haptics->dev, "Unable to request sc IRQ ret=%d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * spmi_haptics_enable - handler to start/stop vibration ++ * @haptics: pointer to haptics struct ++ * @enable: state to set ++ * Returns: 0 on success, < 0 on failure ++ */ ++static int spmi_haptics_enable(struct spmi_haptics *haptics) ++{ ++ int ret; ++ ++ mutex_lock(&haptics->play_lock); ++ if (haptics->sc_count > SC_MAX_COUNT) { ++ dev_err(haptics->dev, "Can't play while in short circuit"); ++ ret = -1; ++ goto out; ++ } ++ ret = spmi_haptics_set_auto_res(haptics, false); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error disabling auto_res, ret=%d\n", ret); ++ goto out; ++ } ++ ++ ret = spmi_haptics_module_enable(haptics, true); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error enabling module, ret=%d\n", ret); ++ goto out; ++ } ++ ++ ret = spmi_haptics_write_play_control(haptics, HAP_PLAY); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error enabling play, ret=%d\n", ret); ++ goto out; ++ } ++ ++ ret = spmi_haptics_set_auto_res(haptics, true); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error enabling auto_res, ret=%d\n", ret); ++ goto out; ++ } ++ ++out: ++ mutex_unlock(&haptics->play_lock); ++ return ret; ++} ++ ++/** ++ * spmi_haptics_enable - handler to start/stop vibration ++ * @haptics: pointer to haptics struct ++ * @enable: state to set ++ * Returns: 0 on success, < 0 on failure ++ */ ++static int spmi_haptics_disable(struct spmi_haptics *haptics) ++{ ++ int ret; ++ ++ mutex_lock(&haptics->play_lock); ++ ++ ret = spmi_haptics_write_play_control(haptics, HAP_STOP); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error disabling play, ret=%d\n", ret); ++ goto out; ++ } ++ ++ ret = spmi_haptics_module_enable(haptics, false); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Error disabling module, ret=%d\n", ret); ++ goto out; ++ } ++ ++out: ++ mutex_unlock(&haptics->play_lock); ++ return ret; ++} ++ ++/* ++ * Threaded function to update the haptics state. ++ */ ++static void spmi_haptics_work(struct work_struct *work) ++{ ++ struct spmi_haptics *haptics = container_of(work, struct spmi_haptics, work); ++ ++ int ret; ++ bool enable; ++ ++ enable = atomic_read(&haptics->active); ++ dev_dbg(haptics->dev, "%s: state: %d\n", __func__, enable); ++ ++ if (enable) ++ ret = spmi_haptics_enable(haptics); ++ else ++ ret = spmi_haptics_disable(haptics); ++ if (ret < 0) ++ dev_err(haptics->dev, "Error setting haptics, ret=%d", ret); ++} ++ ++/** ++ * spmi_haptics_close - callback for input device close ++ * @dev: input device pointer ++ * ++ * Turns off the vibrator. ++ */ ++static void spmi_haptics_close(struct input_dev *dev) ++{ ++ struct spmi_haptics *haptics = input_get_drvdata(dev); ++ ++ cancel_work_sync(&haptics->work); ++ if (atomic_read(&haptics->active)) { ++ atomic_set(&haptics->active, 0); ++ schedule_work(&haptics->work); ++ } ++} ++ ++/** ++ * spmi_haptics_play_effect - play haptics effects ++ * @dev: input device pointer ++ * @data: data of effect ++ * @effect: effect to play ++ */ ++static int spmi_haptics_play_effect(struct input_dev *dev, void *data, ++ struct ff_effect *effect) ++{ ++ struct spmi_haptics *haptics = input_get_drvdata(dev); ++ ++ dev_dbg(haptics->dev, "%s: Rumbling with strong: %d and weak: %d", __func__, ++ effect->u.rumble.strong_magnitude, effect->u.rumble.weak_magnitude); ++ ++ haptics->magnitude = effect->u.rumble.strong_magnitude >> 8; ++ if (!haptics->magnitude) ++ haptics->magnitude = effect->u.rumble.weak_magnitude >> 10; ++ ++ if (!haptics->magnitude) { ++ atomic_set(&haptics->active, 0); ++ goto end; ++ } ++ ++ atomic_set(&haptics->active, 1); ++ ++ haptics->vmax = ((HAP_VMAX_MAX_MV - HAP_VMAX_MIN_MV) * haptics->magnitude) / 100 + ++ HAP_VMAX_MIN_MV; ++ ++ dev_dbg(haptics->dev, "%s: magnitude: %d, vmax: %d", __func__, ++ haptics->magnitude, haptics->vmax); ++ ++ spmi_haptics_write_vmax(haptics); ++ ++end: ++ schedule_work(&haptics->work); ++ ++ return 0; ++} ++ ++static int spmi_haptics_probe(struct platform_device *pdev) ++{ ++ struct spmi_haptics *haptics; ++ struct device_node *node; ++ struct input_dev *input_dev; ++ int ret; ++ u32 val; ++ int i; ++ ++ haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); ++ if (!haptics) ++ return -ENOMEM; ++ ++ haptics->regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!haptics->regmap) ++ return -ENODEV; ++ ++ node = pdev->dev.of_node; ++ ++ haptics->dev = &pdev->dev; ++ ++ ret = of_property_read_u32(node, "reg", &haptics->base); ++ if (ret < 0) { ++ dev_err(haptics->dev, "Couldn't find reg in node = %s ret = %d\n", ++ node->full_name, ret); ++ return ret; ++ } ++ ++ haptics->play_irq = platform_get_irq_byname(pdev, "play"); ++ if (haptics->play_irq < 0) { ++ dev_err(&pdev->dev, "Unable to get play irq\n"); ++ ret = haptics->play_irq; ++ goto register_fail; ++ } ++ ++ haptics->sc_irq = platform_get_irq_byname(pdev, "short"); ++ if (haptics->sc_irq < 0) { ++ dev_err(&pdev->dev, "Unable to get sc irq\n"); ++ ret = haptics->sc_irq; ++ goto register_fail; ++ } ++ ++ // We only support LRAs for now ++ haptics->actuator_type = HAP_TYPE_LRA; ++ ret = of_property_read_u32(node, "qcom,actuator-type", &val); ++ if (!ret) { ++ haptics->actuator_type = val; ++ } ++ ++ // Only buffer mode is currently supported ++ haptics->play_mode = HAP_PLAY_BUFFER; ++ ret = of_property_read_u32(node, "qcom,play-mode", &val); ++ if (!ret) { ++ if (val != HAP_PLAY_BUFFER) { ++ dev_err(&pdev->dev, "qcom,play-mode (%d) isn't supported\n", val); ++ ret = -EINVAL; ++ goto register_fail; ++ } ++ haptics->play_mode = val; ++ } ++ ++ ret = of_property_read_u32(node, "qcom,wave-play-rate-us", &val); ++ if (!ret) { ++ haptics->play_wave_rate = val; ++ } else if (ret != -EINVAL) { ++ dev_err(haptics->dev, "Unable to read play rate ret=%d\n", ret); ++ goto register_fail; ++ } ++ ++ haptics->play_wave_rate = ++ clamp_t(u32, haptics->play_wave_rate, ++ HAP_WAVE_PLAY_RATE_MIN_US, HAP_WAVE_PLAY_RATE_MAX_US); ++ ++ haptics->wave_shape = HAP_WAVE_SINE; ++ ret = of_property_read_u32(node, "qcom,wave-shape", &val); ++ if (!ret) { ++ if (val != HAP_WAVE_SINE && val != HAP_WAVE_SQUARE) { ++ dev_err(&pdev->dev, "qcom,wave-shape is invalid: %d\n", val); ++ ret = -EINVAL; ++ goto register_fail; ++ } ++ haptics->wave_shape = val; ++ } ++ ++ haptics->brake_pat[0] = 0x3; ++ haptics->brake_pat[1] = 0x3; ++ haptics->brake_pat[2] = 0x2; ++ haptics->brake_pat[3] = 0x1; ++ ++ ret = of_property_read_u8_array(node, "qcom,brake-pattern", haptics->brake_pat, 4); ++ if (ret < 0 && ret != -EINVAL) { ++ dev_err(&pdev->dev, "qcom,brake-pattern is invalid, ret = %d\n", ret); ++ goto register_fail; ++ } ++ ++ haptics->boost = ++ devm_gpiod_get_optional(haptics->dev, "qcom,boost", GPIOD_OUT_HIGH); ++ if (IS_ERR(haptics->boost)) { ++ ret = PTR_ERR(haptics->boost); ++ dev_warn(haptics->dev, "Unable to get boost gpio: %d\n", ret); ++ } ++ ++ if (haptics->boost) ++ gpiod_set_value_cansleep(haptics->boost, 1); ++ ++ haptics->current_limit = HAP_ILIM_400_MA; ++ ++ for (i = 0; i < HAP_WAVE_SAMP_LEN; i++) ++ haptics->wave_samp[i] = HAP_WF_SAMP_MAX; ++ ++ ret = spmi_haptics_init(haptics); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Error initialising haptics, ret=%d\n", ++ ret); ++ goto register_fail; ++ } ++ ++ platform_set_drvdata(pdev, haptics); ++ ++ input_dev = devm_input_allocate_device(&pdev->dev); ++ if (!input_dev) ++ return -ENOMEM; ++ ++ INIT_WORK(&haptics->work, spmi_haptics_work); ++ haptics->haptics_input_dev = input_dev; ++ ++ input_dev->name = "spmi_haptics"; ++ input_dev->id.version = 1; ++ input_dev->close = spmi_haptics_close; ++ input_set_drvdata(input_dev, haptics); ++ // Figure out how to make this FF_PERIODIC ++ input_set_capability(haptics->haptics_input_dev, EV_FF, FF_RUMBLE); ++ ++ ret = input_ff_create_memless(input_dev, NULL, ++ spmi_haptics_play_effect); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "couldn't register vibrator as FF device\n"); ++ goto register_fail; ++ } ++ ++ ret = input_register_device(input_dev); ++ if (ret) { ++ dev_err(&pdev->dev, "couldn't register input device\n"); ++ goto register_fail; ++ } ++ ++ return 0; ++ ++register_fail: ++ cancel_work_sync(&haptics->work); ++ mutex_destroy(&haptics->play_lock); ++ ++ return ret; ++} ++ ++static int __maybe_unused spmi_haptics_suspend(struct device *dev) ++{ ++ struct spmi_haptics *haptics = dev_get_drvdata(dev); ++ ++ cancel_work_sync(&haptics->work); ++ spmi_haptics_disable(haptics); ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(spmi_haptics_pm_ops, spmi_haptics_suspend, NULL); ++ ++static void spmi_haptics_remove(struct platform_device *pdev) ++{ ++ struct spmi_haptics *haptics = dev_get_drvdata(&pdev->dev); ++ ++ cancel_work_sync(&haptics->work); ++ mutex_destroy(&haptics->play_lock); ++ input_unregister_device(haptics->haptics_input_dev); ++ ++ if (haptics->boost) ++ gpiod_set_value_cansleep(haptics->boost, 0); ++} ++ ++static void spmi_haptics_shutdown(struct platform_device *pdev) ++{ ++ struct spmi_haptics *haptics = dev_get_drvdata(&pdev->dev); ++ ++ cancel_work_sync(&haptics->work); ++ ++ spmi_haptics_disable(haptics); ++ ++ if (haptics->boost) ++ gpiod_set_value_cansleep(haptics->boost, 0); ++} ++ ++static const struct of_device_id spmi_haptics_match_table[] = { ++ { .compatible = "qcom,spmi-haptics" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, spmi_haptics_match_table); ++ ++static struct platform_driver spmi_haptics_driver = { ++ .probe = spmi_haptics_probe, ++ .remove = spmi_haptics_remove, ++ .shutdown = spmi_haptics_shutdown, ++ .driver = { ++ .name = "spmi-haptics", ++ .pm = &spmi_haptics_pm_ops, ++ .of_match_table = spmi_haptics_match_table, ++ }, ++}; ++module_platform_driver(spmi_haptics_driver); ++ ++MODULE_DESCRIPTION("spmi haptics driver using ff-memless framework"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Caleb Connolly "); +diff --git a/include/dt-bindings/input/qcom,spmi-haptics.h b/include/dt-bindings/input/qcom,spmi-haptics.h +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/include/dt-bindings/input/qcom,spmi-haptics.h +@@ -0,0 +1,31 @@ ++/* ++ * This header provides constants for pmi8998 SPMI haptics options. ++ */ ++ ++#ifndef _DT_BINDINGS_QCOM_PMIC_SPMI_HAPTICS_ ++#define _DT_BINDINGS_QCOM_PMIC_SPMI_HAPTICS_ ++ ++// Actuator types ++#define HAP_TYPE_LRA 0 ++#define HAP_TYPE_ERM 1 ++ ++// LRA Wave type ++#define HAP_WAVE_SINE 0 ++#define HAP_WAVE_SQUARE 1 ++ ++// Play modes ++#define HAP_PLAY_DIRECT 0 ++#define HAP_PLAY_BUFFER 1 ++#define HAP_PLAY_AUDIO 2 ++#define HAP_PLAY_PWM 3 ++ ++#define HAP_PLAY_MAX HAP_PLAY_PWM ++ ++// Auto resonance type ++#define HAP_AUTO_RES_NONE 0 ++#define HAP_AUTO_RES_ZXD 1 ++#define HAP_AUTO_RES_QWD 2 ++#define HAP_AUTO_RES_MAX_QWD 3 ++#define HAP_AUTO_RES_ZXD_EOP 4 ++ ++#endif /* _DT_BINDINGS_QCOM_PMIC_SPMI_HAPTICS_ */ +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0040-arm64-dts-qcom-pm8150b-introduce-spmi-haptics.patch b/patch/kernel/archive/sm8250-6.18/0040-arm64-dts-qcom-pm8150b-introduce-spmi-haptics.patch new file mode 100644 index 000000000000..e58dcbf12dc3 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0040-arm64-dts-qcom-pm8150b-introduce-spmi-haptics.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Mon, 25 Nov 2024 02:17:47 +0700 +Subject: arm64: dts: qcom: pm8150b: introduce spmi haptics + +Add bindings for Qualcomm SPMI haptics on platforms using pm8150b. +Signed-off-by: Teguh Sobirin +--- + arch/arm64/boot/dts/qcom/pm8150b.dtsi | 16 ++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi ++++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +@@ -5,6 +5,7 @@ + */ + + #include ++#include + #include + #include + +@@ -216,5 +217,20 @@ pm8150b_lpg: pwm { + + status = "disabled"; + }; ++ ++ pm8150b_haptics: haptics@c000 { ++ compatible = "qcom,pmi8998-haptics", "qcom,spmi-haptics"; ++ reg = <0xc000>; ++ ++ interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, ++ <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; ++ interrupt-names = "short", "play"; ++ ++ qcom,wave-shape = ; ++ qcom,play-mode = ; ++ qcom,brake-pattern = <0x3 0x3 0x2 0x1>; ++ ++ status = "disabled"; ++ }; + }; + }; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/dt/.placeholder b/patch/kernel/archive/sm8250-6.18/dt/.placeholder new file mode 100644 index 000000000000..e69de29bb2d1 From f6594533c7d6e95c2ada2328ae5730270c539edf Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Wed, 7 Jan 2026 16:35:27 +0000 Subject: [PATCH 2/5] config: kernel: sm8250: edge: copy some configurations from sm8250-6.12, to complete the device functionality. Signed-off-by: CodeChenL <2540735020@qq.com> --- config/kernel/linux-sm8250-edge.config | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/kernel/linux-sm8250-edge.config b/config/kernel/linux-sm8250-edge.config index 4b8471d40bf4..3eabe77f8ae6 100644 --- a/config/kernel/linux-sm8250-edge.config +++ b/config/kernel/linux-sm8250-edge.config @@ -529,9 +529,11 @@ CONFIG_KEYBOARD_ADC=m CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_S6SY761=m +CONFIG_TOUCHSCREEN_NT36523_SPI=m CONFIG_INPUT_MISC=y CONFIG_INPUT_PM8941_PWRKEY=y CONFIG_INPUT_PM8XXX_VIBRATOR=m +CONFIG_INPUT_QCOM_SPMI_HAPTICS=m CONFIG_INPUT_UINPUT=y CONFIG_INPUT_PWM_VIBRA=m # CONFIG_SERIO_SERPORT is not set @@ -585,6 +587,8 @@ CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_SYSCON_REBOOT_MODE=y CONFIG_BATTERY_BQ27XXX=y +CONFIG_CHARGER_QCOM_SMB5=m +CONFIG_BATTERY_QCOM_FG=m CONFIG_SENSORS_ARM_SCMI=y CONFIG_SENSORS_ARM_SCPI=y CONFIG_SENSORS_LM90=m @@ -684,6 +688,7 @@ CONFIG_SND_SOC_APQ8016_SBC=m CONFIG_SND_SOC_SM8250=y CONFIG_SND_SOC_CS35L41_I2C=m CONFIG_SND_SOC_WCD938X_SDW=m +CONFIG_SND_SOC_WSA881X=m CONFIG_SND_SOC_LPASS_WSA_MACRO=y CONFIG_SND_SOC_LPASS_VA_MACRO=y CONFIG_SND_SOC_LPASS_RX_MACRO=m @@ -977,6 +982,7 @@ CONFIG_CRYPTO_SM3_ARM64_CE=m CONFIG_CRYPTO_AES_ARM64=y CONFIG_CRYPTO_AES_ARM64_BS=m CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m CONFIG_CRYPTO_DEV_QCOM_RNG=m CONFIG_CRYPTO_DEV_CCREE=m CONFIG_CRYPTO_DEV_HISI_SEC2=m From 3abd458a2a8de6ae0c5937fb74da026312a43c58 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Thu, 8 Jan 2026 02:55:12 +0000 Subject: [PATCH 3/5] patch: sm8250: edge: add a patche From sm8250-6.12, to fixed touchscreen malfunction after waking from sleep. Signed-off-by: CodeChenL <2540735020@qq.com> --- ...el-turn-on-off-signal-to-touchscreen.patch | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 patch/kernel/archive/sm8250-6.18/0002-drm-dsi-emit-panel-turn-on-off-signal-to-touchscreen.patch diff --git a/patch/kernel/archive/sm8250-6.18/0002-drm-dsi-emit-panel-turn-on-off-signal-to-touchscreen.patch b/patch/kernel/archive/sm8250-6.18/0002-drm-dsi-emit-panel-turn-on-off-signal-to-touchscreen.patch new file mode 100644 index 000000000000..d4d771052f33 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0002-drm-dsi-emit-panel-turn-on-off-signal-to-touchscreen.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jianhua Lu +Date: Mon, 17 Oct 2022 08:02:58 +0800 +Subject: drm: dsi: emit panel turn on/off signal to touchscreen + +--- + drivers/gpu/drm/msm/dsi/dsi_manager.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c +index 111111111111..222222222222 100644 +--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c ++++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c +@@ -7,6 +7,7 @@ + + #include "msm_kms.h" + #include "dsi.h" ++#include "drm/drm_notifier.h" + + #define DSI_CLOCK_MASTER DSI_0 + #define DSI_CLOCK_SLAVE DSI_1 +@@ -273,6 +274,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) + struct mipi_dsi_host *host = msm_dsi->host; + bool is_bonded_dsi = IS_BONDED_DSI(); + int ret; ++ enum drm_notifier_data notifier_data; + + DBG("id=%d", id); + +@@ -286,6 +288,9 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) + return; + } + ++ notifier_data = MI_DRM_BLANK_UNBLANK; ++ mi_drm_notifier_call_chain(MI_DRM_EVENT_BLANK, ¬ifier_data); ++ + ret = msm_dsi_host_enable(host); + if (ret) { + pr_err("%s: enable host %d failed, %d\n", __func__, id, ret); +@@ -329,9 +334,13 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) + struct mipi_dsi_host *host = msm_dsi->host; + bool is_bonded_dsi = IS_BONDED_DSI(); + int ret; ++ enum drm_notifier_data notifier_data; + + DBG("id=%d", id); + ++ notifier_data = MI_DRM_BLANK_POWERDOWN; ++ mi_drm_notifier_call_chain(MI_DRM_EARLY_EVENT_BLANK, ¬ifier_data); ++ + /* + * Do nothing with the host if it is slave-DSI in case of bonded DSI. + * It is safe to call dsi_mgr_phy_disable() here because a single PHY +-- +Armbian + From 5c0f4249c7567739ea5181725ae3e8fdadf20b79 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 11 Jan 2026 08:28:32 +0000 Subject: [PATCH 4/5] sm8250: bump current to 6.18 --- config/sources/families/sm8250.conf | 7 +++---- .../kernel/archive/sm8250-6.18/0000.patching_config.yaml | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/config/sources/families/sm8250.conf b/config/sources/families/sm8250.conf index 69772d2a6e8f..ebd5a81cda77 100644 --- a/config/sources/families/sm8250.conf +++ b/config/sources/families/sm8250.conf @@ -16,14 +16,13 @@ enable_extension "image-output-abl" case $BRANCH in current) - declare -g KERNEL_MAJOR_MINOR="6.12" # Major and minor versions of this kernel. - declare -g KERNELBRANCH='branch:linux-6.12.y' + declare -g KERNEL_MAJOR_MINOR="6.18" # Major and minor versions of this kernel. + declare -g KERNELBRANCH='branch:linux-6.18.y' declare -g -i KERNEL_GIT_CACHE_TTL=120 # 2 minutes; this is a high-traffic repo ;; edge) - declare -g KERNEL_MAJOR_MINOR="6.18" # Major and minor versions of this kernel. - declare -g KERNELBRANCH='branch:linux-6.18.y' + declare -g KERNEL_MAJOR_MINOR="6.19" # Major and minor versions of this kernel. declare -g -i KERNEL_GIT_CACHE_TTL=120 # 2 minutes; this is a high-traffic repo ;; diff --git a/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml b/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml index 38f229666adf..ec326af9e477 100644 --- a/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml +++ b/patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml @@ -1,11 +1,11 @@ -config: # This is file 'patch/kernel/archive/sm8250-6.7/0000.patching_config.yaml' +config: # This is file 'patch/kernel/archive/sm8250-6.18/0000.patching_config.yaml' # Just some info stuff; not used by the patching scripts - name: sm8250-6.12 + name: sm8250-6.18 kind: kernel type: mainline # or: vendor - branch: linux-6.12.y - last-known-good-tag: v6.12.5 + branch: linux-6.18.y + last-known-good-tag: v6.18.3 maintainers: - { github: rpardini, name: Ricardo Pardini, email: ricardo@pardini.net, armbian-forum: rpardini } From 59c1ddb1f1a5178191bfd85e80edc30207dd7769 Mon Sep 17 00:00:00 2001 From: Jiali Chen Date: Sun, 11 Jan 2026 11:21:51 +0000 Subject: [PATCH 5/5] patch: sm8250: edge: add some patches From sm8250-6.12, and made some modifications to be compatible with the latest kernel, to support retroidpocket-rp5 and retroidpocket-rpmini. Signed-off-by: CodeChenL <2540735020@qq.com> --- ...om-Add-venus-firmware-for-OnePlus-8T.patch | 24 + ...m8250-add-uart16-Signed-off-by-Teguh.patch | 53 + ...ver-for-Qualcomm-SMB5-Signed-off-by-.patch | 991 ++++++++++ ...-power-supply-Driver-for-Qualcomm-FG.patch | 1417 ++++++++++++++ ...m8150b-Add-a-charger-Signed-off-by-T.patch | 63 + ...m8150b-Add-a-FG-Signed-off-by-Teguh-.patch | 32 + ...dd-SM8250-Retroid-Pocket-variant-Sig.patch | 1666 +++++++++++++++++ 7 files changed, 4246 insertions(+) create mode 100644 patch/kernel/archive/sm8250-6.18/0029-arm64-dts-qcom-Add-venus-firmware-for-OnePlus-8T.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0031-arm64-dts-qcom-sm8250-add-uart16-Signed-off-by-Teguh.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0032-power-supply-Driver-for-Qualcomm-SMB5-Signed-off-by-.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0033-power-supply-Driver-for-Qualcomm-FG.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0034-arm64-dts-qcom-pm8150b-Add-a-charger-Signed-off-by-T.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0035-arm64-dts-qcom-pm8150b-Add-a-FG-Signed-off-by-Teguh-.patch create mode 100644 patch/kernel/archive/sm8250-6.18/0041-arm64-dts-qcom-add-SM8250-Retroid-Pocket-variant-Sig.patch diff --git a/patch/kernel/archive/sm8250-6.18/0029-arm64-dts-qcom-Add-venus-firmware-for-OnePlus-8T.patch b/patch/kernel/archive/sm8250-6.18/0029-arm64-dts-qcom-Add-venus-firmware-for-OnePlus-8T.patch new file mode 100644 index 000000000000..37c47125a29d --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0029-arm64-dts-qcom-Add-venus-firmware-for-OnePlus-8T.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: amazingfate +Date: Thu, 17 Oct 2024 17:10:02 +0800 +Subject: arm64: dts: qcom: Add venus firmware for OnePlus 8T + +--- + arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts b/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts ++++ b/arch/arm64/boot/dts/qcom/sm8250-oneplus-kebab.dts +@@ -34,3 +34,7 @@ touchscreen@4b { + vcc-supply = <&vreg_l1c_1p8>; + }; + }; ++ ++&venus { ++ firmware-name = "qcom/sm8250/OnePlus/kebab/venus.mbn"; ++}; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0031-arm64-dts-qcom-sm8250-add-uart16-Signed-off-by-Teguh.patch b/patch/kernel/archive/sm8250-6.18/0031-arm64-dts-qcom-sm8250-add-uart16-Signed-off-by-Teguh.patch new file mode 100644 index 000000000000..0bccd7f07de5 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0031-arm64-dts-qcom-sm8250-add-uart16-Signed-off-by-Teguh.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 22:58:09 +0700 +Subject: arm64: dts: qcom: sm8250: add uart16 Signed-off-by: Teguh Sobirin + + +--- + arch/arm64/boot/dts/qcom/sm8250.dtsi | 22 ++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi +@@ -1153,6 +1153,23 @@ spi16: spi@888000 { + status = "disabled"; + }; + ++ uart16: serial@888000 { ++ compatible = "qcom,geni-uart"; ++ reg = <0 0x00888000 0 0x4000>; ++ clock-names = "se"; ++ clocks = <&gcc GCC_QUPV3_WRAP2_S2_CLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&qup_uart16_default>; ++ interrupts = ; ++ power-domains = <&rpmhpd RPMHPD_CX>; ++ operating-points-v2 = <&qup_opp_table>; ++ interconnects = <&qup_virt MASTER_QUP_CORE_2 0 &qup_virt SLAVE_QUP_CORE_2 0>, ++ <&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_QUP_2 0>; ++ interconnect-names = "qup-core", ++ "qup-config"; ++ status = "disabled"; ++ }; ++ + i2c17: i2c@88c000 { + compatible = "qcom,geni-i2c"; + reg = <0 0x0088c000 0 0x4000>; +@@ -5725,6 +5742,11 @@ qup_uart12_default: qup-uart12-default-state { + function = "qup12"; + }; + ++ qup_uart16_default: qup-uart16-default-state { ++ pins = "gpio50", "gpio51"; ++ function = "qup16"; ++ }; ++ + qup_uart17_default: qup-uart17-default-state { + pins = "gpio52", "gpio53", "gpio54", "gpio55"; + function = "qup17"; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0032-power-supply-Driver-for-Qualcomm-SMB5-Signed-off-by-.patch b/patch/kernel/archive/sm8250-6.18/0032-power-supply-Driver-for-Qualcomm-SMB5-Signed-off-by-.patch new file mode 100644 index 000000000000..0d0ceca7d029 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0032-power-supply-Driver-for-Qualcomm-SMB5-Signed-off-by-.patch @@ -0,0 +1,991 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Sun, 24 Nov 2024 23:04:43 +0700 +Subject: power: supply: Driver for Qualcomm SMB5 Signed-off-by: Teguh Sobirin + + +--- + drivers/power/supply/Kconfig | 9 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/qcom_pm8150b_charger.c | 940 ++++++++++ + 3 files changed, 950 insertions(+) + +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index 111111111111..222222222222 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -1008,6 +1008,15 @@ config CHARGER_QCOM_SMB2 + adds support for the SMB2 switch mode battery charger found + in PMI8998 and related PMICs. + ++config CHARGER_QCOM_SMB5 ++ tristate "Qualcomm PMI8998 PMIC charger driver" ++ depends on MFD_SPMI_PMIC ++ depends on IIO ++ help ++ Say Y here to enable the Qualcomm PMIC Charger driver. This ++ adds support for the SMB5 switch mode battery charger found ++ in PM8150B and related PMICs. ++ + config FUEL_GAUGE_MM8013 + tristate "Mitsumi MM8013 fuel gauge driver" + depends on I2C +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -116,4 +116,5 @@ obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o + obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o + obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o + obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o ++obj-$(CONFIG_CHARGER_QCOM_SMB5) += qcom_pm8150b_charger.o + obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o +diff --git a/drivers/power/supply/qcom_pm8150b_charger.c b/drivers/power/supply/qcom_pm8150b_charger.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/power/supply/qcom_pm8150b_charger.c +@@ -0,0 +1,940 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024, Teguh Sobirin . ++ * ++ * This driver is for the switch-mode battery charger power delivery ++ * and boost hardware found in pm8150b and related PMICs. ++ * This work based on pmi8998 charger driver by ++ * Caleb Connolly ++ * Should be merged with the existing charger driver in the future. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* clang-format off */ ++#define BATTERY_CHARGER_STATUS_1 0x06 ++#define ICL_INCR_REQ_FOR_PRECHG_BIT BIT(7) ++#define ZERO_CHARGE_CURRENT_BIT BIT(6) ++#define STEP_CHARGING_STATUS_SHIFT 3 ++#define STEP_CHARGING_STATUS_MASK GENMASK(5, 3) ++#define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) ++ ++#define BATTERY_CHARGER_STATUS_2 0x07 ++#define DROP_IN_BATTERY_VOLTAGE_REFERENCE_BIT BIT(7) ++#define VBATT_LTET_RECHARGE_BIT BIT(6) ++#define VBATT_GTET_INHIBIT_BIT BIT(5) ++#define VBATT_GTET_FLOAT_VOLTAGE_BIT BIT(4) ++#define BATT_GT_FULL_ON_BIT BIT(3) ++#define CHARGER_ERROR_STATUS_SFT_EXPIRE_BIT BIT(2) ++#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(1) ++#define CHARGER_ERROR_STATUS_BAT_TERM_MISSING_BIT BIT(0) ++ ++#define BATTERY_CHARGER_STATUS_4 0x0A ++#define CHARGE_CURRENT_REFERENCE_MASK GENMASK(7, 0) ++ ++#define BATTERY_CHARGER_STATUS_7_REG 0x0D ++#define BAT_TEMP_STATUS_SOFT_LIMIT_MASK GENMASK(5, 4) ++#define BAT_TEMP_STATUS_HOT_SOFT_BIT BIT(5) ++#define BAT_TEMP_STATUS_COLD_SOFT_BIT BIT(4) ++#define BAT_TEMP_STATUS_HARD_LIMIT_MASK GENMASK(3, 2) ++#define BAT_TEMP_STATUS_TOO_HOT_BIT BIT(3) ++#define BAT_TEMP_STATUS_TOO_COLD_BIT BIT(2) ++#define BAT_TEMP_STATUS_TOO_HOT_AFP_BIT BIT(1) ++#define BAT_TEMP_STATUS_TOO_COLD_AFP_BIT BIT(0) ++ ++#define CHARGING_ENABLE_CMD 0x42 ++#define CHARGING_ENABLE_CMD_BIT BIT(0) ++#define CHARGING_ENABLE_POF_BIT BIT(1) ++ ++#define CHGR_CFG2 0x51 ++#define EN_FAVOR_IN_BIT BIT(5) ++#define BAT_OV_ECC_BIT BIT(4) ++#define I_TERM_BIT BIT(3) ++#define AUTO_RECHG_BIT BIT(2) ++#define SOC_BASED_RECHG_BIT BIT(1) ++#define CHARGER_INHIBIT_BIT BIT(0) ++ ++#define PRE_CHARGE_CURRENT_CFG 0x60 ++#define PRE_CHARGE_CURRENT_SETTING_MASK GENMASK(2, 0) ++ ++#define FAST_CHARGE_CURRENT_CFG 0x61 ++#define FAST_CHARGE_CURRENT_SETTING_MASK GENMASK(7, 0) ++ ++#define FLOAT_VOLTAGE_CFG 0x70 ++#define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0) ++ ++#define CHARGE_RCHG_SOC_THRESHOLD_CFG_REG 0x7D ++#define CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK GENMASK(7, 0) ++ ++#define ICL_STATUS 0x107 ++#define ICL_INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) ++ ++#define AICL_STATUS 0x108 ++#define AICL_INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) ++ ++#define POWER_PATH_STATUS 0x10B ++#define P_PATH_INPUT_SS_DONE_BIT BIT(7) ++#define P_PATH_USBIN_SUSPEND_STS_BIT BIT(6) ++#define P_PATH_DCIN_SUSPEND_STS_BIT BIT(5) ++#define P_PATH_USE_USBIN_BIT BIT(4) ++#define P_PATH_USE_DCIN_BIT BIT(3) ++#define P_PATH_POWER_PATH_MASK GENMASK(2, 1) ++#define P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) ++ ++#define OTG_CFG 0x153 ++#define OTG_RESERVED_BIT BIT(7) ++#define FAST_ROLE_SWAP_START_OPTION_BIT BIT(6) ++#define DIS_OTG_ON_TSD_BIT BIT(5) ++#define OTG_CFG_4_BIT BIT(4) ++#define EN_SOC_BASED_OTG_UVLO_BIT BIT(3) ++#define ENABLE_OTG_IN_DEBUG_MODE_BIT BIT(2) ++#define OTG_EN_SRC_CFG_BIT BIT(1) ++#define OTG_HICCUP_CNTR_RST_TIMER_SEL_BIT BIT(0) ++ ++#define APSD_STATUS 0x307 ++#define APSD_STATUS_7_BIT BIT(7) ++#define HVDCP_CHECK_TIMEOUT_BIT BIT(6) ++#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5) ++#define ENUMERATION_DONE_BIT BIT(4) ++#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3) ++#define QC_AUTH_DONE_STATUS_BIT BIT(2) ++#define QC_CHARGER_BIT BIT(1) ++#define APSD_DTC_STATUS_DONE_BIT BIT(0) ++ ++#define APSD_RESULT_STATUS 0x308 ++#define APSD_RESULT_STATUS_7_BIT BIT(7) ++#define APSD_RESULT_STATUS_MASK GENMASK(6, 0) ++#define QC_3P0_BIT BIT(6) ++#define QC_2P0_BIT BIT(5) ++#define FLOAT_CHARGER_BIT BIT(4) ++#define DCP_CHARGER_BIT BIT(3) ++#define CDP_CHARGER_BIT BIT(2) ++#define OCP_CHARGER_BIT BIT(1) ++#define SDP_CHARGER_BIT BIT(0) ++ ++#define USBIN_INT_RT_STS_OFFSET 0x310 ++#define USBIN_PLUGIN_RT_STS_BIT BIT(4) ++ ++#define USBIN_CMD_IL 0x340 ++#define USBIN_SUSPEND_BIT BIT(0) ++ ++#define CMD_APSD 0x341 ++#define APSD_RERUN_BIT BIT(0) ++ ++#define CMD_ICL_OVERRIDE 0x342 ++#define ICL_OVERRIDE_BIT BIT(0) ++ ++#define TYPE_C_CFG 0x358 ++#define BC1P2_START_ON_CC_BIT BIT(7) ++ ++#define HVDCP_PULSE_COUNT_MAX 0x35b ++#define HVDCP_PULSE_COUNT_MAX_QC2_MASK GENMASK(7, 6) ++ ++#define USBIN_ADAPTER_ALLOW_CFG 0x360 ++#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) ++ ++#define USBIN_OPTIONS_1_CFG 0x362 ++#define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6) ++#define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5) ++#define BC1P2_SRC_DETECT_BIT BIT(3) ++#define HVDCP_EN_BIT BIT(2) ++#define HVDCP_NO_AUTH_QC3_CFG_BIT BIT(1) ++ ++#define USBIN_LOAD_CFG 0x365 ++#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) ++#define USBIN_AICL_STEP_TIMING_SEL_MASK GENMASK(3, 2) ++#define USBIN_IN_COLLAPSE_GF_SEL_MASK GENMASK(1, 0) ++ ++#define USBIN_ICL_OPTIONS 0x366 ++#define CFG_USB3P0_SEL_BIT BIT(2) ++#define USB51_MODE_BIT BIT(1) ++#define USBIN_MODE_CHG_BIT BIT(0) ++ ++#define USBIN_CURRENT_LIMIT_CFG 0x370 ++#define USBIN_CURRENT_LIMIT_MASK GENMASK(7, 0) ++ ++#define USBIN_AICL_OPTIONS_CFG 0x380 ++#define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7) ++#define USBIN_AICL_PERIODIC_RERUN_EN_BIT BIT(4) ++#define USBIN_AICL_ADC_EN_BIT BIT(3) ++#define USBIN_AICL_EN_BIT BIT(2) ++ ++#define USBIN_5V_AICL_THRESHOLD_CFG 0x381 ++#define USBIN_5V_AICL_THRESHOLD_CFG_MASK GENMASK(2, 0) ++ ++#define USBIN_CONT_AICL_THRESHOLD_CFG 0x384 ++#define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) ++ ++#define DCIN_CMD_IL 0x440 ++#define DCIN_SUSPEND_BIT BIT(0) ++ ++#define TYPE_C_SNK_STATUS 0x506 ++#define DETECTED_SRC_TYPE_MASK GENMASK(6, 0) ++#define SNK_RP_STD_DAM_BIT BIT(6) ++#define SNK_RP_1P5_DAM_BIT BIT(5) ++#define SNK_RP_3P0_DAM_BIT BIT(4) ++#define SNK_DAM_MASK GENMASK(6, 4) ++#define SNK_DAM_500MA_BIT BIT(6) ++#define SNK_DAM_1500MA_BIT BIT(5) ++#define SNK_DAM_3000MA_BIT BIT(4) ++#define SNK_RP_STD_BIT BIT(3) ++#define SNK_RP_1P5_BIT BIT(2) ++#define SNK_RP_3P0_BIT BIT(1) ++#define SNK_RP_SHORT_BIT BIT(0) ++ ++#define TYPE_C_MODE_CFG 0x544 ++#define TYPEC_TRY_MODE_MASK GENMASK(4, 3) ++#define EN_TRY_SNK_BIT BIT(4) ++#define EN_TRY_SRC_BIT BIT(3) ++#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0) ++#define EN_SRC_ONLY_BIT BIT(2) ++#define EN_SNK_ONLY_BIT BIT(1) ++#define TYPEC_DISABLE_CMD_BIT BIT(0) ++ ++#define TYPEC_TYPE_C_VCONN_CONTROL 0x546 ++#define VCONN_EN_ORIENTATION_BIT BIT(2) ++#define VCONN_EN_VALUE_BIT BIT(1) ++#define VCONN_EN_SRC_BIT BIT(0) ++ ++#define TYPE_C_DEBUG_ACCESS_SINK 0x54a ++#define TYPEC_DEBUG_ACCESS_SINK_MASK GENMASK(4, 0) ++ ++#define DEBUG_ACCESS_SRC_CFG 0x54C ++#define EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) ++ ++#define TYPE_C_EXIT_STATE_CFG 0x550 ++#define BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT BIT(3) ++#define SEL_SRC_UPPER_REF_BIT BIT(2) ++#define EXIT_SNK_BASED_ON_CC_BIT BIT(0) ++ ++#define TYPE_C_INTERRUPT_EN_CFG_1 0x55e ++#define TYPEC_LEGACY_CABLE_INT_EN_BIT BIT(7) ++#define TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT BIT(6) ++#define TYPEC_TRYSOURCE_DETECT_INT_EN_BIT BIT(5) ++#define TYPEC_TRYSINK_DETECT_INT_EN_BIT BIT(4) ++#define TYPEC_CCOUT_DETACH_INT_EN_BIT BIT(3) ++#define TYPEC_CCOUT_ATTACH_INT_EN_BIT BIT(2) ++#define TYPEC_VBUS_DEASSERT_INT_EN_BIT BIT(1) ++#define TYPEC_VBUS_ASSERT_INT_EN_BIT BIT(0) ++ ++#define BARK_BITE_WDOG_PET 0x643 ++#define BARK_BITE_WDOG_PET_BIT BIT(0) ++ ++#define WD_CFG 0x651 ++#define WATCHDOG_TRIGGER_AFP_EN_BIT BIT(7) ++#define BARK_WDOG_INT_EN_BIT BIT(6) ++#define BITE_WDOG_INT_EN_BIT BIT(5) ++#define SFT_AFTER_WDOG_IRQ_MASK GENMASK(4, 3) ++#define WDOG_IRQ_SFT_BIT BIT(2) ++#define WDOG_TIMER_EN_ON_PLUGIN_BIT BIT(1) ++#define WDOG_TIMER_EN_BIT BIT(0) ++ ++#define SNARL_BARK_BITE_WD_CFG 0x653 ++#define BITE_WDOG_DISABLE_CHARGING_CFG_BIT BIT(7) ++#define SNARL_WDOG_TIMEOUT_MASK GENMASK(6, 4) ++#define BARK_WDOG_TIMEOUT_MASK GENMASK(3, 2) ++#define BITE_WDOG_TIMEOUT_MASK GENMASK(1, 0) ++ ++#define AICL_RERUN_TIME_CFG 0x661 ++#define AICL_RERUN_TIME_MASK GENMASK(1, 0) ++ ++#define SDP_CURRENT_UA 500000 ++#define CDP_CURRENT_UA 3000000 ++#define DCP_CURRENT_UA 3300000 ++#define CURRENT_MAX_UA DCP_CURRENT_UA ++ ++/* pmi8150b registers represent current in increments of 1/40th of an amp */ ++#define CURRENT_SCALE_FACTOR 50000 ++/* clang-format on */ ++ ++enum charger_status { ++ TRICKLE_CHARGE = 0, ++ PRE_CHARGE, ++ FAST_CHARGE, ++ FULLON_CHARGE, ++ TAPER_CHARGE, ++ TERMINATE_CHARGE, ++ INHIBIT_CHARGE, ++ DISABLE_CHARGE, ++}; ++ ++struct smb5_register { ++ u16 addr; ++ u8 mask; ++ u8 val; ++}; ++ ++/** ++ * struct smb5_chip - smb5 chip structure ++ * @dev: Device reference for power_supply ++ * @name: The platform device name ++ * @base: Base address for smb5 registers ++ * @regmap: Register map ++ * @batt_info: Battery data from DT ++ * @status_change_work: Worker to handle plug/unplug events ++ * @cable_irq: USB plugin IRQ ++ * @wakeup_enabled: If the cable IRQ will cause a wakeup ++ * @usb_in_i_chan: USB_IN current measurement channel ++ * @usb_in_v_chan: USB_IN voltage measurement channel ++ * @chg_psy: Charger power supply instance ++ */ ++struct smb5_chip { ++ struct device *dev; ++ const char *name; ++ unsigned int base; ++ struct regmap *regmap; ++ struct power_supply_battery_info *batt_info; ++ ++ struct delayed_work status_change_work; ++ int cable_irq; ++ bool wakeup_enabled; ++ ++ struct iio_channel *usb_in_i_chan; ++ struct iio_channel *usb_in_v_chan; ++ ++ struct power_supply *chg_psy; ++}; ++ ++static enum power_supply_property smb5_properties[] = { ++ POWER_SUPPLY_PROP_MANUFACTURER, ++ POWER_SUPPLY_PROP_MODEL_NAME, ++ POWER_SUPPLY_PROP_CURRENT_MAX, ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_ONLINE, ++ POWER_SUPPLY_PROP_USB_TYPE, ++}; ++ ++static int smb5_get_prop_usb_online(struct smb5_chip *chip, int *val) ++{ ++ unsigned int stat; ++ int rc; ++ ++ rc = regmap_read(chip->regmap, chip->base + USBIN_INT_RT_STS_OFFSET, &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Couldn't read USBIN_RT_STS rc=%d\n", rc); ++ return rc; ++ } ++ ++ *val = (stat & USBIN_PLUGIN_RT_STS_BIT); ++ return 0; ++} ++ ++/* ++ * Qualcomm "automatic power source detection" aka APSD ++ * tells us what type of charger we're connected to. ++ */ ++static int smb5_apsd_get_charger_type(struct smb5_chip *chip, int *val) ++{ ++ unsigned int apsd_stat, stat; ++ int usb_online = 0; ++ int rc; ++ ++ rc = smb5_get_prop_usb_online(chip, &usb_online); ++ if (!usb_online) { ++ *val = POWER_SUPPLY_USB_TYPE_UNKNOWN; ++ return rc; ++ } ++ ++ rc = regmap_read(chip->regmap, chip->base + APSD_STATUS, &apsd_stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Failed to read apsd status, rc = %d", rc); ++ return rc; ++ } ++ if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) { ++ dev_err(chip->dev, "Apsd not ready"); ++ return -EAGAIN; ++ } ++ ++ rc = regmap_read(chip->regmap, chip->base + APSD_RESULT_STATUS, &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Failed to read apsd result, rc = %d", rc); ++ return rc; ++ } ++ ++ stat &= APSD_RESULT_STATUS_MASK; ++ ++ if (stat & CDP_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_CDP; ++ } else if (stat & DCP_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & OCP_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & FLOAT_CHARGER_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & QC_2P0_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else if (stat & QC_3P0_BIT){ ++ *val = POWER_SUPPLY_USB_TYPE_DCP; ++ } else { ++ *val = POWER_SUPPLY_USB_TYPE_SDP; ++ } ++ ++ return 0; ++} ++ ++static int smb5_get_prop_status(struct smb5_chip *chip, int *val) ++{ ++ unsigned char stat[2]; ++ int usb_online = 0; ++ int rc; ++ ++ rc = smb5_get_prop_usb_online(chip, &usb_online); ++ if (!usb_online) { ++ *val = POWER_SUPPLY_STATUS_DISCHARGING; ++ return rc; ++ } ++ ++ rc = regmap_bulk_read(chip->regmap, ++ chip->base + BATTERY_CHARGER_STATUS_1, &stat, 2); ++ if (rc < 0) { ++ dev_err(chip->dev, "Failed to read charging status ret=%d\n", ++ rc); ++ return rc; ++ } ++ ++ if (stat[1] & VBATT_GTET_INHIBIT_BIT) { ++ *val = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ return 0; ++ } ++ ++ stat[0] = stat[0] & BATTERY_CHARGER_STATUS_MASK; ++ ++ switch (stat[0]) { ++ case TRICKLE_CHARGE: ++ case PRE_CHARGE: ++ case FAST_CHARGE: ++ case FULLON_CHARGE: ++ case TAPER_CHARGE: ++ *val = POWER_SUPPLY_STATUS_CHARGING; ++ return rc; ++ case DISABLE_CHARGE: ++ *val = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ return rc; ++ case TERMINATE_CHARGE: ++ case INHIBIT_CHARGE: ++ *val = POWER_SUPPLY_STATUS_FULL; ++ return rc; ++ default: ++ *val = POWER_SUPPLY_STATUS_UNKNOWN; ++ return rc; ++ } ++} ++ ++static inline int smb5_get_current_limit(struct smb5_chip *chip, ++ unsigned int *val) ++{ ++ int rc = regmap_read(chip->regmap, chip->base + AICL_STATUS, val); ++ ++ if (rc >= 0) ++ *val *= CURRENT_SCALE_FACTOR; ++ return rc; ++} ++ ++static int smb5_set_current_limit(struct smb5_chip *chip, unsigned int val) ++{ ++ unsigned char val_raw; ++ ++ if (val > 4950000) { ++ dev_err(chip->dev, ++ "Can't set current limit higher than 4950000uA"); ++ return -EINVAL; ++ } ++ val_raw = val / CURRENT_SCALE_FACTOR; ++ ++ return regmap_write(chip->regmap, chip->base + USBIN_CURRENT_LIMIT_CFG, ++ val_raw); ++} ++ ++static void smb5_status_change_work(struct work_struct *work) ++{ ++ unsigned int charger_type, current_ua; ++ int usb_online = 0; ++ int count, rc; ++ struct smb5_chip *chip; ++ ++ chip = container_of(work, struct smb5_chip, status_change_work.work); ++ ++ smb5_get_prop_usb_online(chip, &usb_online); ++ if (!usb_online) ++ return; ++ ++ for (count = 0; count < 3; count++) { ++ dev_dbg(chip->dev, "get charger type retry %d\n", count); ++ rc = smb5_apsd_get_charger_type(chip, &charger_type); ++ if (rc != -EAGAIN) ++ break; ++ msleep(100); ++ } ++ ++ if (rc < 0 && rc != -EAGAIN) { ++ dev_err(chip->dev, "get charger type failed: %d\n", rc); ++ return; ++ } ++ ++ if (rc < 0) { ++ rc = regmap_update_bits(chip->regmap, chip->base + CMD_APSD, ++ APSD_RERUN_BIT, APSD_RERUN_BIT); ++ schedule_delayed_work(&chip->status_change_work, ++ msecs_to_jiffies(1000)); ++ dev_dbg(chip->dev, "get charger type failed, rerun apsd\n"); ++ return; ++ } ++ ++ switch (charger_type) { ++ case POWER_SUPPLY_USB_TYPE_CDP: ++ current_ua = CDP_CURRENT_UA; ++ break; ++ case POWER_SUPPLY_USB_TYPE_DCP: ++ current_ua = DCP_CURRENT_UA; ++ break; ++ case POWER_SUPPLY_USB_TYPE_SDP: ++ default: ++ current_ua = SDP_CURRENT_UA; ++ break; ++ } ++ ++ smb5_set_current_limit(chip, current_ua); ++ power_supply_changed(chip->chg_psy); ++} ++ ++static int smb5_get_iio_chan(struct smb5_chip *chip, struct iio_channel *chan, ++ int *val) ++{ ++ int rc; ++ union power_supply_propval status; ++ ++ rc = power_supply_get_property(chip->chg_psy, POWER_SUPPLY_PROP_STATUS, ++ &status); ++ if (rc < 0 || status.intval != POWER_SUPPLY_STATUS_CHARGING) { ++ *val = 0; ++ return 0; ++ } ++ ++ if (IS_ERR(chan)) { ++ dev_err(chip->dev, "Failed to chan, err = %li", PTR_ERR(chan)); ++ return PTR_ERR(chan); ++ } ++ ++ return iio_read_channel_processed(chan, val); ++} ++ ++static int smb5_get_prop_health(struct smb5_chip *chip, int *val) ++{ ++ int rc; ++ unsigned int stat; ++ ++ rc = regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, ++ &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Couldn't read charger status rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) { ++ dev_err(chip->dev, "battery over-voltage"); ++ } ++ ++ rc = regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_7_REG, ++ &stat); ++ if (rc < 0) { ++ dev_err(chip->dev, "Couldn't read charger status 7 rc=%d\n", rc); ++ return rc; ++ } ++ ++ if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) ++ *val = POWER_SUPPLY_HEALTH_COLD; ++ else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT) ++ *val = POWER_SUPPLY_HEALTH_OVERHEAT; ++ else if (stat & BAT_TEMP_STATUS_COLD_SOFT_BIT) ++ *val = POWER_SUPPLY_HEALTH_COOL; ++ else if (stat & BAT_TEMP_STATUS_HOT_SOFT_BIT) ++ *val = POWER_SUPPLY_HEALTH_WARM; ++ else ++ *val = POWER_SUPPLY_HEALTH_GOOD; ++ ++ return 0; ++} ++ ++static int smb5_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct smb5_chip *chip = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_MANUFACTURER: ++ val->strval = "Qualcomm"; ++ return 0; ++ case POWER_SUPPLY_PROP_MODEL_NAME: ++ val->strval = chip->name; ++ return 0; ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return smb5_get_current_limit(chip, &val->intval); ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ return smb5_get_iio_chan(chip, chip->usb_in_i_chan, ++ &val->intval); ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ return smb5_get_iio_chan(chip, chip->usb_in_v_chan, ++ &val->intval); ++ case POWER_SUPPLY_PROP_ONLINE: ++ return smb5_get_prop_usb_online(chip, &val->intval); ++ case POWER_SUPPLY_PROP_STATUS: ++ return smb5_get_prop_status(chip, &val->intval); ++ case POWER_SUPPLY_PROP_HEALTH: ++ return smb5_get_prop_health(chip, &val->intval); ++ case POWER_SUPPLY_PROP_USB_TYPE: ++ return smb5_apsd_get_charger_type(chip, &val->intval); ++ default: ++ dev_err(chip->dev, "invalid property: %d\n", psp); ++ return -EINVAL; ++ } ++} ++ ++static int smb5_set_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct smb5_chip *chip = power_supply_get_drvdata(psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return smb5_set_current_limit(chip, val->intval); ++ default: ++ dev_err(chip->dev, "No setter for property: %d\n", psp); ++ return -EINVAL; ++ } ++} ++ ++static int smb5_property_is_writable(struct power_supply *psy, ++ enum power_supply_property psp) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_CURRENT_MAX: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++static irqreturn_t smb5_handle_batt_overvoltage(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ unsigned int status; ++ ++ regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, ++ &status); ++ ++ if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) { ++ /* The hardware stops charging automatically */ ++ dev_err(chip->dev, "battery overvoltage detected\n"); ++ power_supply_changed(chip->chg_psy); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t smb5_handle_usb_plugin(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ ++ power_supply_changed(chip->chg_psy); ++ ++ schedule_delayed_work(&chip->status_change_work, ++ msecs_to_jiffies(1500)); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t smb5_handle_usb_icl_change(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ ++ power_supply_changed(chip->chg_psy); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t smb5_handle_wdog_bark(int irq, void *data) ++{ ++ struct smb5_chip *chip = data; ++ int rc; ++ ++ power_supply_changed(chip->chg_psy); ++ ++ rc = regmap_write(chip->regmap, BARK_BITE_WDOG_PET, ++ BARK_BITE_WDOG_PET_BIT); ++ if (rc < 0) ++ dev_err(chip->dev, "Couldn't pet the dog rc=%d\n", rc); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct power_supply_desc smb5_psy_desc = { ++ .name = "pmi8998_charger", ++ .type = POWER_SUPPLY_TYPE_USB, ++ .usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) | ++ BIT(POWER_SUPPLY_USB_TYPE_CDP) | ++ BIT(POWER_SUPPLY_USB_TYPE_DCP) | ++ BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN), ++ .properties = smb5_properties, ++ .num_properties = ARRAY_SIZE(smb5_properties), ++ .get_property = smb5_get_property, ++ .set_property = smb5_set_property, ++ .property_is_writeable = smb5_property_is_writable, ++}; ++ ++/* Init sequence derived from vendor downstream driver */ ++static const struct smb5_register smb5_init_seq[] = { ++ { .addr = USBIN_CMD_IL, .mask = USBIN_SUSPEND_BIT, .val = 0 }, ++ { .addr = AICL_RERUN_TIME_CFG, .mask = AICL_RERUN_TIME_MASK, .val = 0 }, ++ /* ++ * By default configure us as an upstream facing port ++ * FIXME: This will be handled by the type-c driver ++ */ ++ { .addr = TYPE_C_MODE_CFG, ++ .mask = EN_TRY_SNK_BIT | EN_SNK_ONLY_BIT, ++ .val = EN_TRY_SNK_BIT }, ++ { .addr = TYPEC_TYPE_C_VCONN_CONTROL, ++ .mask = VCONN_EN_ORIENTATION_BIT | VCONN_EN_SRC_BIT | ++ VCONN_EN_VALUE_BIT, ++ .val = VCONN_EN_SRC_BIT }, ++ { .addr = DEBUG_ACCESS_SRC_CFG, ++ .mask = EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT, ++ .val = EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT }, ++ { .addr = TYPE_C_EXIT_STATE_CFG, ++ .mask = SEL_SRC_UPPER_REF_BIT, ++ .val = SEL_SRC_UPPER_REF_BIT }, ++ /* ++ * Disable Type-C factory mode and stay in Attached.SRC state when VCONN ++ * over-current happens ++ */ ++ { .addr = TYPE_C_CFG, ++ .mask = BC1P2_START_ON_CC_BIT, ++ .val = 0 }, ++ { .addr = TYPE_C_DEBUG_ACCESS_SINK, ++ .mask = TYPEC_DEBUG_ACCESS_SINK_MASK, ++ .val = 0x17 }, ++ /* Configure VBUS for software control */ ++ { .addr = OTG_CFG, .mask = OTG_EN_SRC_CFG_BIT, .val = 0 }, ++ /* ++ * Use VBAT to determine the recharge threshold when battery is full ++ * rather than the state of charge. ++ */ ++ { .addr = CHARGE_RCHG_SOC_THRESHOLD_CFG_REG, ++ .mask = CHARGE_RCHG_SOC_THRESHOLD_CFG_MASK, ++ .val = 98 }, ++ /* Enable charging */ ++ { .addr = CHARGING_ENABLE_CMD, ++ .mask = CHARGING_ENABLE_CMD_BIT, ++ .val = CHARGING_ENABLE_CMD_BIT }, ++ /* Enable BC1P2 Src detect */ ++ { .addr = USBIN_OPTIONS_1_CFG, ++ .mask = BC1P2_SRC_DETECT_BIT, ++ .val = BC1P2_SRC_DETECT_BIT }, ++ /* Set the default SDP charger type to a 500ma USB 2.0 port */ ++ { .addr = USBIN_ICL_OPTIONS, ++ .mask = USBIN_MODE_CHG_BIT, ++ .val = USBIN_MODE_CHG_BIT }, ++ { .addr = CMD_ICL_OVERRIDE, ++ .mask = ICL_OVERRIDE_BIT, ++ .val = 0 }, ++ { .addr = USBIN_LOAD_CFG, ++ .mask = ICL_OVERRIDE_AFTER_APSD_BIT, ++ .val = 0 }, ++ /* Disable watchdog */ ++ { .addr = SNARL_BARK_BITE_WD_CFG, .mask = 0xff, .val = 0 }, ++ { .addr = WD_CFG, ++ .mask = WATCHDOG_TRIGGER_AFP_EN_BIT | WDOG_TIMER_EN_ON_PLUGIN_BIT | ++ BARK_WDOG_INT_EN_BIT, ++ .val = 0 }, ++ /* ++ * Enable Automatic Input Current Limit, this will slowly ramp up the current ++ * When connected to a wall charger, and automatically stop when it detects ++ * the charger current limit (voltage drop?) or it reaches the programmed limit. ++ */ ++ { .addr = USBIN_AICL_OPTIONS_CFG, ++ .mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT ++ | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT, ++ .val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT ++ | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT }, ++}; ++ ++static int smb5_init_hw(struct smb5_chip *chip) ++{ ++ int rc, i; ++ for (i = 0; i < ARRAY_SIZE(smb5_init_seq); i++) { ++ dev_dbg(chip->dev, "%d: Writing 0x%02x to 0x%02x\n", i, ++ smb5_init_seq[i].val, smb5_init_seq[i].addr); ++ rc = regmap_update_bits(chip->regmap, ++ chip->base + smb5_init_seq[i].addr, ++ smb5_init_seq[i].mask, ++ smb5_init_seq[i].val); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, ++ "%s: init command %d failed\n", ++ __func__, i); ++ } ++ return 0; ++} ++ ++static int smb5_init_irq(struct smb5_chip *chip, int *irq, const char *name, ++ irqreturn_t (*handler)(int irq, void *data)) ++{ ++ int irqnum; ++ int rc; ++ ++ irqnum = platform_get_irq_byname(to_platform_device(chip->dev), name); ++ if (irqnum < 0) ++ return irqnum; ++ ++ rc = devm_request_threaded_irq(chip->dev, irqnum, NULL, handler, ++ IRQF_ONESHOT, name, chip); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, "Couldn't request irq %s\n", ++ name); ++ ++ if (irq) ++ *irq = irqnum; ++ ++ return 0; ++} ++ ++static int smb5_probe(struct platform_device *pdev) ++{ ++ struct power_supply_config supply_config = {}; ++ struct power_supply_desc *desc; ++ struct smb5_chip *chip; ++ int rc, irq; ++ ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ chip->dev = &pdev->dev; ++ chip->name = pdev->name; ++ ++ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!chip->regmap) ++ return dev_err_probe(chip->dev, -ENODEV, ++ "failed to locate the regmap\n"); ++ ++ rc = device_property_read_u32(chip->dev, "reg", &chip->base); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, ++ "Couldn't read base address\n"); ++ ++ chip->usb_in_v_chan = devm_iio_channel_get(chip->dev, "usb_in_v_div_16"); ++ if (IS_ERR(chip->usb_in_v_chan)) ++ return dev_err_probe(chip->dev, PTR_ERR(chip->usb_in_v_chan), ++ "Couldn't get usb_in_v_div_16 IIO channel\n"); ++ ++ chip->usb_in_i_chan = devm_iio_channel_get(chip->dev, "usb_in_i_uv"); ++ if (IS_ERR(chip->usb_in_i_chan)) { ++ return dev_err_probe(chip->dev, PTR_ERR(chip->usb_in_i_chan), ++ "Couldn't get usb_in_i_uv IIO channel\n"); ++ } ++ ++ rc = smb5_init_hw(chip); ++ if (rc < 0) ++ return rc; ++ ++ supply_config.drv_data = chip; ++ supply_config.fwnode = dev_fwnode(&pdev->dev); ++ ++ desc = devm_kzalloc(chip->dev, sizeof(smb5_psy_desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ memcpy(desc, &smb5_psy_desc, sizeof(smb5_psy_desc)); ++ desc->name = ++ devm_kasprintf(chip->dev, GFP_KERNEL, "%s-charger", ++ (const char *)device_get_match_data(chip->dev)); ++ if (!desc->name) ++ return -ENOMEM; ++ ++ chip->chg_psy = ++ devm_power_supply_register(chip->dev, desc, &supply_config); ++ if (IS_ERR(chip->chg_psy)) ++ return dev_err_probe(chip->dev, PTR_ERR(chip->chg_psy), ++ "failed to register power supply\n"); ++ ++ rc = power_supply_get_battery_info(chip->chg_psy, &chip->batt_info); ++ if (rc) ++ return dev_err_probe(chip->dev, rc, ++ "Failed to get battery info\n"); ++ ++ rc = devm_delayed_work_autocancel(chip->dev, &chip->status_change_work, ++ smb5_status_change_work); ++ if (rc) ++ return dev_err_probe(chip->dev, rc, ++ "Failed to init status change work\n"); ++ ++ rc = (chip->batt_info->voltage_max_design_uv - 3487500) / 7500 + 1; ++ rc = regmap_update_bits(chip->regmap, chip->base + FLOAT_VOLTAGE_CFG, ++ FLOAT_VOLTAGE_SETTING_MASK, rc); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, "Couldn't set vbat max\n"); ++ ++ rc = smb5_init_irq(chip, &irq, "bat-ov", smb5_handle_batt_overvoltage); ++ if (rc < 0) ++ return rc; ++ ++ rc = smb5_init_irq(chip, &chip->cable_irq, "usbin-plugin", ++ smb5_handle_usb_plugin); ++ if (rc < 0) ++ return rc; ++ ++ rc = smb5_init_irq(chip, &irq, "usbin-icl-change", ++ smb5_handle_usb_icl_change); ++ if (rc < 0) ++ return rc; ++ rc = smb5_init_irq(chip, &irq, "wdog-bark", smb5_handle_wdog_bark); ++ if (rc < 0) ++ return rc; ++ ++ rc = dev_pm_set_wake_irq(chip->dev, chip->cable_irq); ++ if (rc < 0) ++ return dev_err_probe(chip->dev, rc, "Couldn't set wake irq\n"); ++ ++ platform_set_drvdata(pdev, chip); ++ ++ /* Initialise charger state */ ++ schedule_delayed_work(&chip->status_change_work, 0); ++ ++ return 0; ++} ++ ++static const struct of_device_id smb5_match_id_table[] = { ++ { .compatible = "qcom,pm8150b-charger", .data = "pm8150b" }, ++ { /* sentinal */ } ++}; ++MODULE_DEVICE_TABLE(of, smb5_match_id_table); ++ ++static struct platform_driver qcom_spmi_smb5 = { ++ .probe = smb5_probe, ++ .driver = { ++ .name = "qcom-pm8150b-charger", ++ .of_match_table = smb5_match_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_spmi_smb5); ++ ++MODULE_AUTHOR("Caleb Connolly "); ++MODULE_DESCRIPTION("Qualcomm SMB5 Charger Driver"); ++MODULE_LICENSE("GPL"); +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0033-power-supply-Driver-for-Qualcomm-FG.patch b/patch/kernel/archive/sm8250-6.18/0033-power-supply-Driver-for-Qualcomm-FG.patch new file mode 100644 index 000000000000..052b6a9f3225 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0033-power-supply-Driver-for-Qualcomm-FG.patch @@ -0,0 +1,1417 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:00:52 +0700 +Subject: power: supply: Driver for Qualcomm FG + +support for pm8150 taken from: +https://github.com/map220v/sm8150-mainline +Signed-off-by: Teguh Sobirin +--- + drivers/power/supply/Kconfig | 8 + + drivers/power/supply/Makefile | 1 + + drivers/power/supply/qcom_fg.c | 1364 ++++++++++ + 3 files changed, 1373 insertions(+) + +diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig +index 111111111111..222222222222 100644 +--- a/drivers/power/supply/Kconfig ++++ b/drivers/power/supply/Kconfig +@@ -1017,6 +1017,14 @@ config CHARGER_QCOM_SMB5 + adds support for the SMB5 switch mode battery charger found + in PM8150B and related PMICs. + ++config BATTERY_QCOM_FG ++ tristate "Qualcomm PMIC fuel gauge driver" ++ depends on MFD_SPMI_PMIC ++ help ++ Say Y here to enable the Qualcomm PMIC Fuel Gauge driver. This ++ adds support for battery fuel gauging and state of charge of ++ battery connected to the fuel gauge. ++ + config FUEL_GAUGE_MM8013 + tristate "Mitsumi MM8013 fuel gauge driver" + depends on I2C +diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile +index 111111111111..222222222222 100644 +--- a/drivers/power/supply/Makefile ++++ b/drivers/power/supply/Makefile +@@ -117,4 +117,5 @@ obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o + obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o + obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o + obj-$(CONFIG_CHARGER_QCOM_SMB5) += qcom_pm8150b_charger.o ++obj-$(CONFIG_BATTERY_QCOM_FG) += qcom_fg.o + obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o +diff --git a/drivers/power/supply/qcom_fg.c b/drivers/power/supply/qcom_fg.c +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/drivers/power/supply/qcom_fg.c +@@ -0,0 +1,1364 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SOC */ ++#define BATT_MONOTONIC_SOC 0x009 ++ ++/* BATT */ ++#define PARAM_ADDR_BATT_TEMP 0x150 ++#define BATT_INFO_JEITA_COLD 0x162 ++#define BATT_INFO_JEITA_COOL 0x163 ++#define BATT_INFO_JEITA_WARM 0x164 ++#define BATT_INFO_JEITA_HOT 0x165 ++#define PARAM_ADDR_BATT_VOLTAGE 0x1a0 ++#define PARAM_ADDR_BATT_CURRENT 0x1a2 ++ ++/* RRADC */ ++#define ADC_RR_BATT_TEMP_LSB 0x288 ++ ++/* MEMIF */ ++#define MEM_INTF_STS(chip) (chip->ops->memif_base + 0x10) ++#define MEM_INTF_CFG(chip) (chip->ops->memif_base + 0x50) ++#define MEM_INTF_CTL(chip) (chip->ops->memif_base + 0x51) ++#define MEM_INTF_IMA_CFG(chip) (chip->ops->memif_base + 0x52) ++#define MEM_INTF_IMA_EXP_STS(chip) (chip->ops->memif_base + 0x55) ++#define MEM_INTF_IMA_HW_STS(chip) (chip->ops->memif_base + 0x56) ++#define MEM_INTF_IMA_ERR_STS(chip) (chip->ops->memif_base + 0x5f) ++#define MEM_INTF_IMA_BYTE_EN(chip) (chip->ops->memif_base + 0x60) ++#define MEM_INTF_ADDR_LSB(chip) (chip->ops->memif_base + 0x61) ++#define MEM_INTF_RD_DATA0(chip) (chip->ops->memif_base + 0x67) ++#define MEM_INTF_WR_DATA0(chip) (chip->ops->memif_base + 0x63) ++#define MEM_IF_DMA_STS(chip) (chip->ops->memif_base + 0x70) ++#define MEM_IF_DMA_CTL(chip) (chip->ops->memif_base + 0x71) ++ ++/* SRAM addresses */ ++#define TEMP_THRESHOLD 0x454 ++#define BATT_TEMP 0x550 ++#define BATT_VOLTAGE_CURRENT 0x5cc ++ ++#define BATT_TEMP_LSB_MASK GENMASK(7, 0) ++#define BATT_TEMP_MSB_MASK GENMASK(2, 0) ++ ++#define BATT_TEMP_JEITA_COLD 100 ++#define BATT_TEMP_JEITA_COOL 50 ++#define BATT_TEMP_JEITA_WARM 400 ++#define BATT_TEMP_JEITA_HOT 450 ++ ++#define MEM_INTF_AVAIL BIT(0) ++#define MEM_INTF_CTL_BURST BIT(7) ++#define MEM_INTF_CTL_WR_EN BIT(6) ++#define RIF_MEM_ACCESS_REQ BIT(7) ++ ++#define MEM_IF_TIMEOUT_MS 5000 ++#define SRAM_ACCESS_RELEASE_DELAY_MS 500 ++ ++struct qcom_fg_chip; ++ ++struct qcom_fg_ops { ++ int (*get_capacity)(struct qcom_fg_chip *chip, int *); ++ int (*get_temperature)(struct qcom_fg_chip *chip, int *); ++ int (*get_current)(struct qcom_fg_chip *chip, int *); ++ int (*get_voltage)(struct qcom_fg_chip *chip, int *); ++ int (*get_temp_threshold)(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int *); ++ int (*set_temp_threshold)(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int); ++ ++ short memif_base; ++}; ++ ++struct qcom_fg_chip { ++ struct device *dev; ++ unsigned int base; ++ struct regmap *regmap; ++ const struct qcom_fg_ops *ops; ++ struct notifier_block nb; ++ ++ struct power_supply *batt_psy; ++ struct power_supply_battery_info *batt_info; ++ struct power_supply *chg_psy; ++ int status; ++ struct delayed_work status_changed_work; ++ ++ struct completion sram_access_granted; ++ struct completion sram_access_revoked; ++ struct workqueue_struct *sram_wq; ++ struct delayed_work sram_release_access_work; ++ spinlock_t sram_request_lock; ++ spinlock_t sram_rw_lock; ++ int sram_requests; ++}; ++ ++/************************ ++ * IO FUNCTIONS ++ * **********************/ ++ ++/** ++ * @brief qcom_fg_read() - Read multiple registers with regmap_bulk_read ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to read values into ++ * @param addr Address to read from ++ * @param len Number of registers (bytes) to read ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_read(struct qcom_fg_chip *chip, u8 *val, u16 addr, int len) ++{ ++ if (((chip->base + addr) & 0xff00) == 0) ++ return -EINVAL; ++ ++ dev_vdbg(chip->dev, "%s: Reading 0x%x bytes from 0x%x", __func__, len, addr); ++ ++ return regmap_bulk_read(chip->regmap, chip->base + addr, val, len); ++} ++ ++/** ++ * @brief qcom_fg_write() - Write multiple registers with regmap_bulk_write ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to write values from ++ * @param addr Address to write to ++ * @param len Number of registers (bytes) to write ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_write(struct qcom_fg_chip *chip, u8 *val, u16 addr, int len) ++{ ++ bool sec_access = (addr & 0xff) > 0xd0; ++ u8 sec_addr_val = 0xa5; ++ int ret; ++ ++ if (((chip->base + addr) & 0xff00) == 0) ++ return -EINVAL; ++ ++ dev_vdbg(chip->dev, "%s: Writing 0x%x to 0x%x", __func__, *val, addr); ++ ++ if (sec_access) { ++ ret = regmap_bulk_write(chip->regmap, ++ ((chip->base + addr) & 0xff00) | 0xd0, ++ &sec_addr_val, 1); ++ if (ret) ++ return ret; ++ } ++ ++ return regmap_bulk_write(chip->regmap, chip->base + addr, val, len); ++} ++ ++/** ++ * @brief qcom_fg_masked_write() - like qcom_fg_write but applies ++ * a mask first. ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to write values from ++ * @param addr Address to write to ++ * @param len Number of registers (bytes) to write ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_masked_write(struct qcom_fg_chip *chip, u16 addr, u8 mask, u8 val) ++{ ++ u8 reg; ++ int ret; ++ ++ ret = qcom_fg_read(chip, ®, addr, 1); ++ if (ret) ++ return ret; ++ ++ reg &= ~mask; ++ reg |= val & mask; ++ ++ return qcom_fg_write(chip, ®, addr, 1); ++} ++ ++/************************ ++ * SRAM FUNCTIONS ++ * **********************/ ++ ++/** ++ * @brief qcom_fg_sram_check_access() - Check if SRAM is accessible ++ * ++ * @param chip Pointer to chip ++ * @return bool true if accessible, false otherwise ++ */ ++static bool qcom_fg_sram_check_access(struct qcom_fg_chip *chip) ++{ ++ u8 mem_if_status; ++ int ret; ++ ++ ret = qcom_fg_read(chip, &mem_if_status, ++ MEM_INTF_STS(chip), 1); ++ ++ if (ret || !(mem_if_status & MEM_INTF_AVAIL)) ++ return false; ++ ++ ret = qcom_fg_read(chip, &mem_if_status, ++ MEM_INTF_CFG(chip), 1); ++ ++ if (ret) ++ return false; ++ ++ return !!(mem_if_status & RIF_MEM_ACCESS_REQ); ++} ++ ++/** ++ * @brief qcom_fg_sram_request_access() - Request access to SRAM and wait for it ++ * ++ * @param chip Pointer to chip ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_request_access(struct qcom_fg_chip *chip) ++{ ++ bool sram_accessible; ++ int ret; ++ ++ spin_lock(&chip->sram_request_lock); ++ ++ sram_accessible = qcom_fg_sram_check_access(chip); ++ ++ dev_vdbg(chip->dev, "Requesting SRAM access, current state: %d, requests: %d\n", ++ sram_accessible, chip->sram_requests); ++ ++ if (!sram_accessible && chip->sram_requests == 0) { ++ ret = qcom_fg_masked_write(chip, MEM_INTF_CFG(chip), ++ RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set SRAM access request bit: %d\n", ret); ++ ++ spin_unlock(&chip->sram_request_lock); ++ return ret; ++ } ++ } ++ ++ chip->sram_requests++; ++ ++ spin_unlock(&chip->sram_request_lock); ++ ++ /* Wait to get access to SRAM, and try again if interrupted */ ++ do { ++ ret = wait_for_completion_interruptible_timeout( ++ &chip->sram_access_granted, ++ msecs_to_jiffies(MEM_IF_TIMEOUT_MS)); ++ } while(ret == -ERESTARTSYS); ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ ++ spin_lock(&chip->sram_request_lock); ++ chip->sram_requests--; ++ spin_unlock(&chip->sram_request_lock); ++ } else { ++ ret = 0; ++ ++ reinit_completion(&chip->sram_access_revoked); ++ } ++ ++ return ret; ++} ++ ++/** ++ * @brief qcom_fg_sram_release_access() - Release access to SRAM ++ * ++ * @param chip Pointer to chip ++ * @return int 0 on success, negative errno on error ++ */ ++static void qcom_fg_sram_release_access(struct qcom_fg_chip *chip) ++{ ++ spin_lock(&chip->sram_request_lock); ++ ++ chip->sram_requests--; ++ ++ if(WARN(chip->sram_requests < 0, ++ "sram_requests=%d, cannot be negative! resetting to 0.\n", ++ chip->sram_requests)) ++ chip->sram_requests = 0; ++ ++ if(chip->sram_requests == 0) ++ /* Schedule access release */ ++ queue_delayed_work(chip->sram_wq, &chip->sram_release_access_work, ++ msecs_to_jiffies(SRAM_ACCESS_RELEASE_DELAY_MS)); ++ ++ spin_unlock(&chip->sram_request_lock); ++} ++ ++static void qcom_fg_sram_release_access_worker(struct work_struct *work) ++{ ++ struct qcom_fg_chip *chip; ++ bool wait = false; ++ int ret; ++ ++ chip = container_of(work, struct qcom_fg_chip, sram_release_access_work.work); ++ ++ spin_lock(&chip->sram_request_lock); ++ ++ /* Request access release if there are still no access requests */ ++ if(chip->sram_requests == 0) { ++ qcom_fg_masked_write(chip, MEM_INTF_CFG(chip), RIF_MEM_ACCESS_REQ, 0); ++ wait = true; ++ } ++ ++ spin_unlock(&chip->sram_request_lock); ++ ++ if(!wait) ++ return; ++ ++ /* Wait for SRAM access to be released, and try again if interrupted */ ++ do { ++ ret = wait_for_completion_interruptible_timeout( ++ &chip->sram_access_revoked, ++ msecs_to_jiffies(MEM_IF_TIMEOUT_MS)); ++ } while(ret == -ERESTARTSYS); ++ ++ reinit_completion(&chip->sram_access_granted); ++} ++ ++/** ++ * @brief qcom_fg_sram_config_access() - Configure access to SRAM ++ * ++ * @param chip Pointer to chip ++ * @param write 0 for read access, 1 for write access ++ * @param burst 1 to access mutliple addresses successively ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_config_access(struct qcom_fg_chip *chip, ++ bool write, bool burst) ++{ ++ u8 intf_ctl; ++ int ret; ++ ++ intf_ctl = (write ? MEM_INTF_CTL_WR_EN : 0) ++ | (burst ? MEM_INTF_CTL_BURST : 0); ++ ++ ret = qcom_fg_write(chip, &intf_ctl, ++ MEM_INTF_CTL(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to configure SRAM access: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_sram_read() - Read data from SRAM ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to read values into ++ * @param addr Address to read from ++ * @param len Number of bytes to read ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_read(struct qcom_fg_chip *chip, ++ u8 *val, u16 addr, int len, int offset) ++{ ++ u8 *rd_data = val; ++ int ret = 0; ++ ++ ret = qcom_fg_sram_request_access(chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed to request SRAM access: %d", ret); ++ return ret; ++ } ++ ++ spin_lock(&chip->sram_rw_lock); ++ ++ dev_vdbg(chip->dev, ++ "Reading address 0x%x with offset %d of length %d from SRAM", ++ addr, len, offset); ++ ++ ret = qcom_fg_sram_config_access(chip, 0, (len > 4)); ++ if (ret) { ++ dev_err(chip->dev, "Failed to configure SRAM access: %d", ret); ++ goto out; ++ } ++ ++ while(len > 0) { ++ /* Set SRAM address register */ ++ ret = qcom_fg_write(chip, (u8 *) &addr, ++ MEM_INTF_ADDR_LSB(chip), 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to set SRAM address: %d", ret); ++ goto out; ++ } ++ ++ ret = qcom_fg_read(chip, rd_data, ++ MEM_INTF_RD_DATA0(chip) + offset, len); ++ ++ addr += 4; ++ ++ if (ret) ++ goto out; ++ ++ rd_data += 4 - offset; ++ len -= 4 - offset; ++ offset = 0; ++ } ++out: ++ spin_unlock(&chip->sram_rw_lock); ++ qcom_fg_sram_release_access(chip); ++ ++ return ret; ++} ++ ++/** ++ * @brief qcom_fg_sram_write() - Write data to SRAM ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to write values from ++ * @param addr Address to write to ++ * @param len Number of bytes to write ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_sram_write(struct qcom_fg_chip *chip, ++ u8 *val, u16 addr, int len, int offset) ++{ ++ u8 *wr_data = val; ++ int ret; ++ ++ ret = qcom_fg_sram_request_access(chip); ++ if (ret) { ++ dev_err(chip->dev, "Failed to request SRAM access: %d", ret); ++ return ret; ++ } ++ ++ spin_lock(&chip->sram_rw_lock); ++ ++ dev_vdbg(chip->dev, ++ "Wrtiting address 0x%x with offset %d of length %d to SRAM", ++ addr, len, offset); ++ ++ ret = qcom_fg_sram_config_access(chip, 1, (len > 4)); ++ if (ret) { ++ dev_err(chip->dev, "Failed to configure SRAM access: %d", ret); ++ goto out; ++ } ++ ++ while(len > 0) { ++ /* Set SRAM address register */ ++ ret = qcom_fg_write(chip, (u8 *) &addr, ++ MEM_INTF_ADDR_LSB(chip), 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to set SRAM address: %d", ret); ++ goto out; ++ } ++ ++ ret = qcom_fg_write(chip, wr_data, ++ MEM_INTF_WR_DATA0(chip) + offset, len); ++ ++ addr += 4; ++ ++ if (ret) ++ goto out; ++ ++ wr_data += 4 - offset; ++ len -= 4 - offset; ++ offset = 0; ++ } ++out: ++ spin_unlock(&chip->sram_rw_lock); ++ qcom_fg_sram_release_access(chip); ++ ++ return ret; ++} ++ ++/************************* ++ * BATTERY STATUS ++ * ***********************/ ++ ++/** ++ * @brief qcom_fg_get_capacity() - Get remaining capacity of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_capacity(struct qcom_fg_chip *chip, int *val) ++{ ++ u8 cap[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, cap, BATT_MONOTONIC_SOC, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read capacity: %d", ret); ++ return ret; ++ } ++ ++ if (cap[0] != cap[1]) { ++ cap[0] = cap[0] < cap[1] ? cap[0] : cap[1]; ++ } ++ ++ *val = DIV_ROUND_CLOSEST((cap[0] - 1) * 98, 0xff - 2) + 1; ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_temperature() - Get temperature of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_temperature(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_sram_read(chip, readval, BATT_TEMP, 2, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read temperature: %d", ret); ++ return ret; ++ } ++ ++ temp = readval[1] << 8 | readval[0]; ++ *val = temp * 625 / 1000 - 2730; ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_current() - Get current being drawn from battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_current(struct qcom_fg_chip *chip, int *val) ++{ ++ s16 temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_sram_read(chip, readval, BATT_VOLTAGE_CURRENT, 2, 3); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read current: %d", ret); ++ return ret; ++ } ++ ++ temp = (s16)(readval[1] << 8 | readval[0]); ++ *val = div_s64((s64)temp * 152587, 1000); ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_voltage() - Get voltage of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_voltage(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_sram_read(chip, readval, BATT_VOLTAGE_CURRENT, 2, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read voltage: %d", ret); ++ return ret; ++ } ++ ++ temp = readval[1] << 8 | readval[0]; ++ *val = div_u64((u64)temp * 152587, 1000); ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_get_temp_threshold() - Get configured temperature thresholds ++ * ++ * @param chip Pointer to chip ++ * @param psp Power supply property of temperature limit ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_get_temp_threshold(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int *val) ++{ ++ u8 temp; ++ int offset; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ offset = 0; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ offset = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ offset = 2; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ offset = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = qcom_fg_sram_read(chip, &temp, TEMP_THRESHOLD, 1, offset); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to read JEITA property %d level: %d\n", psp, ret); ++ return ret; ++ } ++ ++ *val = (temp - 30) * 10; ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_set_temp_threshold() - Configure temperature thresholds ++ * ++ * @param chip Pointer to chip ++ * @param psp Power supply property of temperature limit ++ * @param val Pointer to get value from ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_set_temp_threshold(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int val) ++{ ++ u8 temp; ++ int offset; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ offset = 0; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ offset = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ offset = 2; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ offset = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ temp = val / 10 + 30; ++ ++ ret = qcom_fg_sram_write(chip, &temp, TEMP_THRESHOLD, 1, offset); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to write JEITA property %d level: %d\n", psp, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/************************* ++ * BATTERY STATUS, GEN3 ++ * ***********************/ ++ ++/** ++ * @brief qcom_fg_gen3_get_temperature() - Get temperature of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_temperature(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, PARAM_ADDR_BATT_TEMP, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read temperature: %d\n", ret); ++ return ret; ++ } ++ ++ temp = ((readval[1] & BATT_TEMP_MSB_MASK) << 8) | ++ (readval[0] & BATT_TEMP_LSB_MASK); ++ temp = DIV_ROUND_CLOSEST(temp * 10, 4); ++ ++ *val = temp -2730; ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_gen3_get_current() - Get current being drawn from battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_current(struct qcom_fg_chip *chip, int *val) ++{ ++ s16 temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, PARAM_ADDR_BATT_CURRENT, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read current: %d\n", ret); ++ return ret; ++ } ++ ++ //handle rev 1 too ++ temp = (s16)(readval[1] << 8 | readval[0]); ++ *val = div_s64((s64)temp * 488281, 1000); ++ ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_gen3_get_voltage() - Get voltage of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_voltage(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, PARAM_ADDR_BATT_VOLTAGE, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read voltage: %d\n", ret); ++ return ret; ++ } ++ ++ //handle rev 1 too ++ temp = readval[1] << 8 | readval[0]; ++ *val = div_u64((u64)temp * 122070, 1000); ++ return 0; ++} ++ ++/** ++ * @brief qcom_fg_gen3_get_temp_threshold() - Get configured temperature thresholds ++ * ++ * @param chip Pointer to chip ++ * @param psp Power supply property of temperature limit ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen3_get_temp_threshold(struct qcom_fg_chip *chip, ++ enum power_supply_property psp, int *val) ++{ ++ u8 temp; ++ u16 reg; ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ reg = BATT_INFO_JEITA_COLD; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ reg = BATT_INFO_JEITA_HOT; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ reg = BATT_INFO_JEITA_COOL; ++ break; ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ reg = BATT_INFO_JEITA_WARM; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = qcom_fg_read(chip, &temp, reg, 1); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to read JEITA property %d level: %d\n", psp, ret); ++ return ret; ++ } ++ ++ /* Resolution is 0.5C. Base is -30C. */ ++ *val = (((5 * temp) / 10) - 30) * 10; ++ return 0; ++} ++ ++/************************* ++ * BATTERY STATUS, GEN4 ++ * ***********************/ ++ ++/** ++ * @brief qcom_fg_gen4_get_temperature() - Get temperature of battery ++ * ++ * @param chip Pointer to chip ++ * @param val Pointer to store value at ++ * @return int 0 on success, negative errno on error ++ */ ++static int qcom_fg_gen4_get_temperature(struct qcom_fg_chip *chip, int *val) ++{ ++ int temp; ++ u8 readval[2]; ++ int ret; ++ ++ ret = qcom_fg_read(chip, readval, ADC_RR_BATT_TEMP_LSB, 2); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read temperature: %d", ret); ++ return ret; ++ } ++ ++ temp = readval[1] << 8 | readval[0]; ++ *val = temp * 10; ++ return 0; ++} ++ ++/************************ ++ * BATTERY POWER SUPPLY ++ * **********************/ ++ ++/* Pre-Gen3 fuel gauge. PMI8996 and older */ ++static const struct qcom_fg_ops ops_fg = { ++ .get_capacity = qcom_fg_get_capacity, ++ .get_temperature = qcom_fg_get_temperature, ++ .get_current = qcom_fg_get_current, ++ .get_voltage = qcom_fg_get_voltage, ++ .get_temp_threshold = qcom_fg_get_temp_threshold, ++ .set_temp_threshold = qcom_fg_set_temp_threshold, ++ .memif_base = 0x400, ++}; ++ ++/* Gen3 fuel gauge. PMI8998 and newer */ ++static const struct qcom_fg_ops ops_fg_gen3 = { ++ .get_capacity = qcom_fg_get_capacity, ++ .get_temperature = qcom_fg_gen3_get_temperature, ++ .get_current = qcom_fg_gen3_get_current, ++ .get_voltage = qcom_fg_gen3_get_voltage, ++ .get_temp_threshold = qcom_fg_gen3_get_temp_threshold, ++ .memif_base = 0x400, ++}; ++ ++/* Gen4 fuel gauge. PM8150B and newer */ ++static const struct qcom_fg_ops ops_fg_gen4 = { ++ .get_capacity = qcom_fg_get_capacity, ++ .get_temperature = qcom_fg_gen4_get_temperature, ++ .get_current = qcom_fg_gen3_get_current, ++ .get_voltage = qcom_fg_gen3_get_voltage, ++ .get_temp_threshold = qcom_fg_gen3_get_temp_threshold, ++ .memif_base = 0x300, ++}; ++ ++static enum power_supply_property qcom_fg_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_CAPACITY, ++ POWER_SUPPLY_PROP_CURRENT_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_TEMP_MIN, ++ POWER_SUPPLY_PROP_TEMP_MAX, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MIN, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MAX, ++}; ++ ++static int qcom_fg_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct qcom_fg_chip *chip = power_supply_get_drvdata(psy); ++ int temp, ret = 0; ++ ++ dev_dbg(chip->dev, "Getting property: %d", psp); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ /* Get status from charger if available */ ++ if (chip->chg_psy && ++ chip->status != POWER_SUPPLY_STATUS_UNKNOWN) { ++ val->intval = chip->status; ++ break; ++ } else { ++ /* ++ * Fall back to capacity and current-based ++ * status checking ++ */ ++ ret = chip->ops->get_capacity(chip, &temp); ++ if (ret) { ++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; ++ break; ++ } ++ if (temp == 100) { ++ val->intval = POWER_SUPPLY_STATUS_FULL; ++ break; ++ } ++ ++ ret = chip->ops->get_current(chip, &temp); ++ if (ret) { ++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; ++ break; ++ } ++ if (temp < 0) ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ else if (temp > 0) ++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; ++ else ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ } ++ ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LION; ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ ret = chip->ops->get_capacity(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_NOW: ++ ret = chip->ops->get_current(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ ret = chip->ops->get_voltage(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = chip->batt_info->voltage_min_design_uv; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = chip->batt_info->voltage_max_design_uv; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: ++ val->intval = chip->batt_info->charge_full_design_uah; ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = 1; ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ ret = chip->ops->get_temperature(chip, &val->intval); ++ break; ++ case POWER_SUPPLY_PROP_TEMP_MIN: ++ case POWER_SUPPLY_PROP_TEMP_MAX: ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ ret = chip->ops->get_temp_threshold(chip, psp, &val->intval); ++ break; ++ default: ++ dev_err(chip->dev, "invalid property: %d\n", psp); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static const struct power_supply_desc batt_psy_desc = { ++ .name = "qcom-battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = qcom_fg_props, ++ .num_properties = ARRAY_SIZE(qcom_fg_props), ++ .get_property = qcom_fg_get_property, ++}; ++ ++/******************** ++ * INIT FUNCTIONS ++ * ******************/ ++ ++static int qcom_fg_iacs_clear_sequence(struct qcom_fg_chip *chip) ++{ ++ u8 temp; ++ int ret; ++ ++ /* clear the error */ ++ ret = qcom_fg_masked_write(chip, MEM_INTF_IMA_CFG(chip), BIT(2), BIT(2)); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write IMA_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ temp = 0x4; ++ ret = qcom_fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write MEM_INTF_ADDR_MSB: %d\n", ret); ++ return ret; ++ } ++ ++ temp = 0x0; ++ ret = qcom_fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write WR_DATA3: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write RD_DATA3: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_masked_write(chip, MEM_INTF_IMA_CFG(chip), BIT(2), 0); ++ if (ret) { ++ dev_err(chip->dev, "Failed to write IMA_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_fg_clear_ima(struct qcom_fg_chip *chip, ++ bool check_hw_sts) ++{ ++ u8 err_sts, exp_sts, hw_sts; ++ bool run_err_clr_seq = false; ++ int ret; ++ ++ ret = qcom_fg_read(chip, &err_sts, ++ MEM_INTF_IMA_ERR_STS(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read IMA_ERR_STS: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_read(chip, &exp_sts, ++ MEM_INTF_IMA_EXP_STS(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read IMA_EXP_STS: %d\n", ret); ++ return ret; ++ } ++ ++ if (check_hw_sts) { ++ ret = qcom_fg_read(chip, &hw_sts, ++ MEM_INTF_IMA_HW_STS(chip), 1); ++ if (ret) { ++ dev_err(chip->dev, "Failed to read IMA_HW_STS: %d\n", ret); ++ return ret; ++ } ++ /* ++ * Lower nibble should be equal to upper nibble before SRAM ++ * transactions begins from SW side. ++ */ ++ if ((hw_sts & 0x0f) != hw_sts >> 4) { ++ dev_dbg(chip->dev, "IMA HW not in correct state, hw_sts=%x\n", ++ hw_sts); ++ run_err_clr_seq = true; ++ } ++ } ++ ++ if (exp_sts & (BIT(0) | BIT(1) | BIT(3) | ++ BIT(4) | BIT(5) | BIT(6) | ++ BIT(7))) { ++ dev_dbg(chip->dev, "IMA exception bit set, exp_sts=%x\n", exp_sts); ++ run_err_clr_seq = true; ++ } ++ ++ if (run_err_clr_seq) { ++ ret = qcom_fg_iacs_clear_sequence(chip); ++ if (!ret) ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t qcom_fg_handle_soc_delta(int irq, void *data) ++{ ++ struct qcom_fg_chip *chip = data; ++ ++ /* Signal change in state of charge */ ++ power_supply_changed(chip->batt_psy); ++ dev_dbg(chip->dev, "SOC changed"); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t qcom_fg_handle_mem_avail(int irq, void *data) ++{ ++ struct qcom_fg_chip *chip = data; ++ ++ if (qcom_fg_sram_check_access(chip)) { ++ complete_all(&chip->sram_access_granted); ++ dev_dbg(chip->dev, "SRAM access granted"); ++ } else { ++ complete_all(&chip->sram_access_revoked); ++ dev_dbg(chip->dev, "SRAM access revoked"); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void qcom_fg_status_changed_worker(struct work_struct *work) ++{ ++ struct qcom_fg_chip *chip = container_of(work, struct qcom_fg_chip, ++ status_changed_work.work); ++ ++ power_supply_changed(chip->batt_psy); ++} ++ ++static int qcom_fg_notifier_call(struct notifier_block *nb, ++ unsigned long val, void *v) ++{ ++ struct qcom_fg_chip *chip = container_of(nb, struct qcom_fg_chip, nb); ++ struct power_supply *psy = v; ++ union power_supply_propval propval; ++ int ret; ++ ++ if (psy == chip->chg_psy) { ++ ret = power_supply_get_property(psy, ++ POWER_SUPPLY_PROP_STATUS, &propval); ++ if (ret) ++ chip->status = POWER_SUPPLY_STATUS_UNKNOWN; ++ ++ chip->status = propval.intval; ++ ++ power_supply_changed(chip->batt_psy); ++ ++ if (chip->status == POWER_SUPPLY_STATUS_UNKNOWN) { ++ /* ++ * REVISIT: Find better solution or remove current-based ++ * status checking once checking is properly implemented ++ * in charger drivers ++ ++ * Sometimes it take a while for current to stabilize, ++ * so signal property change again later to make sure ++ * current-based status is properly detected. ++ */ ++ cancel_delayed_work_sync(&chip->status_changed_work); ++ schedule_delayed_work(&chip->status_changed_work, ++ msecs_to_jiffies(1000)); ++ } ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static int qcom_fg_probe(struct platform_device *pdev) ++{ ++ struct power_supply_config supply_config = {}; ++ struct qcom_fg_chip *chip; ++ const __be32 *prop_addr; ++ int irq; ++ u8 dma_status; ++ bool error_present; ++ int ret; ++ ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ chip->dev = &pdev->dev; ++ chip->ops = of_device_get_match_data(&pdev->dev); ++ ++ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); ++ if (!chip->regmap) { ++ dev_err(chip->dev, "Failed to locate the regmap\n"); ++ return -ENODEV; ++ } ++ ++ /* Get base address */ ++ prop_addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); ++ if (!prop_addr) { ++ dev_err(chip->dev, "Failed to read SOC base address from dt\n"); ++ return -EINVAL; ++ } ++ chip->base = be32_to_cpu(*prop_addr); ++ ++ /* ++ * Change the FG_MEM_INT interrupt to track IACS_READY ++ * condition instead of end-of-transaction. This makes sure ++ * that the next transaction starts only after the hw is ready. ++ * IACS_INTR_SRC_SLCT is BIT(3) ++ */ ++ ret = qcom_fg_masked_write(chip, ++ MEM_INTF_IMA_CFG(chip), BIT(3), BIT(3)); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to configure interrupt sourete: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_fg_clear_ima(chip, true); ++ if (ret && ret != -EAGAIN) { ++ dev_err(chip->dev, "Failed to clear IMA exception: %d\n", ret); ++ return ret; ++ } ++ ++ /* Check and clear DMA errors */ ++ ret = qcom_fg_read(chip, &dma_status, MEM_IF_DMA_STS(chip), 1); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to read dma_status: %d\n", ret); ++ return ret; ++ } ++ ++ error_present = dma_status & (BIT(1) | BIT(2)); ++ ret = qcom_fg_masked_write(chip, MEM_IF_DMA_CTL(chip), BIT(0), ++ error_present ? BIT(0) : 0); ++ if (ret < 0) { ++ dev_err(chip->dev, "Failed to write dma_ctl: %d\n", ret); ++ return ret; ++ } ++ ++ supply_config.drv_data = chip; ++ supply_config.fwnode = dev_fwnode(&pdev->dev); ++ ++ chip->batt_psy = devm_power_supply_register(chip->dev, ++ &batt_psy_desc, &supply_config); ++ if (IS_ERR(chip->batt_psy)) { ++ if (PTR_ERR(chip->batt_psy) != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Failed to register battery\n"); ++ return PTR_ERR(chip->batt_psy); ++ } ++ ++ platform_set_drvdata(pdev, chip); ++ ++ ret = power_supply_get_battery_info(chip->batt_psy, &chip->batt_info); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to get battery info: %d\n", ret); ++ return ret; ++ } ++ ++ /* Initialize SRAM */ ++ if (of_device_is_compatible(pdev->dev.of_node, "qcom,pmi8994-fg")) { ++ irq = of_irq_get_byname(pdev->dev.of_node, "mem-avail"); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "Failed to get irq mem-avail byname: %d\n", ++ irq); ++ return irq; ++ } ++ ++ init_completion(&chip->sram_access_granted); ++ init_completion(&chip->sram_access_revoked); ++ ++ chip->sram_wq = create_singlethread_workqueue("qcom_fg"); ++ INIT_DELAYED_WORK(&chip->sram_release_access_work, ++ qcom_fg_sram_release_access_worker); ++ ++ ret = devm_request_threaded_irq(chip->dev, irq, NULL, ++ qcom_fg_handle_mem_avail, ++ IRQF_ONESHOT, "mem-avail", chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to request mem-avail IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ spin_lock_init(&chip->sram_request_lock); ++ spin_lock_init(&chip->sram_rw_lock); ++ chip->sram_requests = 0; ++ } ++ ++ /* Set default temperature thresholds */ ++ if (chip->ops->set_temp_threshold) { ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_MIN, ++ BATT_TEMP_JEITA_COLD); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set cold threshold: %d\n", ret); ++ return ret; ++ } ++ ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_MAX, ++ BATT_TEMP_JEITA_WARM); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set warm threshold: %d\n", ret); ++ return ret; ++ } ++ ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MIN, ++ BATT_TEMP_JEITA_COOL); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set cool threshold: %d\n", ret); ++ return ret; ++ } ++ ++ ret = chip->ops->set_temp_threshold(chip, ++ POWER_SUPPLY_PROP_TEMP_ALERT_MAX, ++ BATT_TEMP_JEITA_HOT); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to set hot threshold: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ /* Get soc-delta IRQ */ ++ irq = of_irq_get_byname(pdev->dev.of_node, "soc-delta"); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "Failed to get irq soc-delta byname: %d\n", ++ irq); ++ return irq; ++ } ++ ++ ret = devm_request_threaded_irq(chip->dev, irq, NULL, ++ qcom_fg_handle_soc_delta, ++ IRQF_ONESHOT, "soc-delta", chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Failed to request soc-delta IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ /* Optional: Get charger power supply for status checking */ ++ chip->chg_psy = devm_power_supply_get_by_reference(chip->dev, ++ "power-supplies"); ++ if (IS_ERR(chip->chg_psy)) { ++ ret = PTR_ERR(chip->chg_psy); ++ dev_warn(chip->dev, "Failed to get charger supply: %d\n", ret); ++ chip->chg_psy = NULL; ++ } ++ ++ if (chip->chg_psy) { ++ INIT_DELAYED_WORK(&chip->status_changed_work, ++ qcom_fg_status_changed_worker); ++ ++ chip->nb.notifier_call = qcom_fg_notifier_call; ++ ret = power_supply_reg_notifier(&chip->nb); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to register notifier: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void qcom_fg_remove(struct platform_device *pdev) ++{ ++ struct qcom_fg_chip *chip = platform_get_drvdata(pdev); ++ ++ power_supply_put_battery_info(chip->batt_psy, chip->batt_info); ++ ++ if(chip->sram_wq) ++ destroy_workqueue(chip->sram_wq); ++} ++ ++static const struct of_device_id fg_match_id_table[] = { ++ { .compatible = "qcom,pmi8994-fg", .data = &ops_fg }, ++ { .compatible = "qcom,pmi8998-fg", .data = &ops_fg_gen3 }, ++ { .compatible = "qcom,pm8150b-fg", .data = &ops_fg_gen4 }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, fg_match_id_table); ++ ++static struct platform_driver qcom_fg_driver = { ++ .probe = qcom_fg_probe, ++ .remove = qcom_fg_remove, ++ .driver = { ++ .name = "qcom-fg", ++ .of_match_table = fg_match_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_fg_driver); ++ ++MODULE_AUTHOR("Caleb Connolly "); ++MODULE_AUTHOR("Joel Selvaraj "); ++MODULE_AUTHOR("Yassine Oudjana "); ++MODULE_DESCRIPTION("Qualcomm PMIC Fuel Gauge Driver"); ++MODULE_LICENSE("GPL v2"); +\ No newline at end of file +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0034-arm64-dts-qcom-pm8150b-Add-a-charger-Signed-off-by-T.patch b/patch/kernel/archive/sm8250-6.18/0034-arm64-dts-qcom-pm8150b-Add-a-charger-Signed-off-by-T.patch new file mode 100644 index 000000000000..4facdb16f9de --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0034-arm64-dts-qcom-pm8150b-Add-a-charger-Signed-off-by-T.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:01:45 +0700 +Subject: arm64: dts: qcom: pm8150b: Add a charger Signed-off-by: Teguh Sobirin + + +--- + arch/arm64/boot/dts/qcom/pm8150b.dtsi | 32 ++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi ++++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +@@ -107,6 +107,26 @@ pm8150b_temp: temp-alarm@2400 { + #thermal-sensor-cells = <0>; + }; + ++ pm8150b_charger: charger@1000 { ++ compatible = "qcom,pm8150b-charger"; ++ reg = <0x1000>; ++ ++ interrupts = <0x2 0x13 0x4 IRQ_TYPE_EDGE_BOTH>, ++ <0x2 0x12 0x2 IRQ_TYPE_EDGE_BOTH>, ++ <0x2 0x16 0x1 IRQ_TYPE_EDGE_RISING>, ++ <0x2 0x13 0x7 IRQ_TYPE_EDGE_RISING>; ++ interrupt-names = "usbin-plugin", ++ "bat-ov", ++ "wdog-bark", ++ "usbin-icl-change"; ++ ++ io-channels = <&pm8150b_adc 7>, ++ <&pm8150b_adc 8>; ++ io-channel-names = "usb_in_i_uv", "usb_in_v_div_16"; ++ ++ status = "disabled"; ++ }; ++ + pm8150b_adc: adc@3100 { + compatible = "qcom,spmi-adc5"; + reg = <0x3100>; +@@ -133,6 +153,18 @@ channel@6 { + label = "die_temp"; + }; + ++ channel@7 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ label = "usb_in_i_uv"; ++ }; ++ ++ channel@8 { ++ reg = ; ++ qcom,pre-scaling = <1 16>; ++ label = "usb_in_v_div_16"; ++ }; ++ + channel@9 { + reg = ; + qcom,pre-scaling = <1 1>; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0035-arm64-dts-qcom-pm8150b-Add-a-FG-Signed-off-by-Teguh-.patch b/patch/kernel/archive/sm8250-6.18/0035-arm64-dts-qcom-pm8150b-Add-a-FG-Signed-off-by-Teguh-.patch new file mode 100644 index 000000000000..188da5346aa6 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0035-arm64-dts-qcom-pm8150b-Add-a-FG-Signed-off-by-Teguh-.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 20 Nov 2024 23:02:30 +0700 +Subject: arm64: dts: qcom: pm8150b: Add a FG Signed-off-by: Teguh Sobirin + + +--- + arch/arm64/boot/dts/qcom/pm8150b.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +index 111111111111..222222222222 100644 +--- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi ++++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi +@@ -127,6 +127,14 @@ pm8150b_charger: charger@1000 { + status = "disabled"; + }; + ++ pm8150b_fg: fuel-gauge@4000 { ++ compatible = "qcom,pm8150b-fg"; ++ reg = <0x4000>; ++ interrupts = <0x2 0x40 0x3 IRQ_TYPE_EDGE_RISING>; ++ interrupt-names = "soc-delta"; ++ status = "disabled"; ++ }; ++ + pm8150b_adc: adc@3100 { + compatible = "qcom,spmi-adc5"; + reg = <0x3100>; +-- +Armbian + diff --git a/patch/kernel/archive/sm8250-6.18/0041-arm64-dts-qcom-add-SM8250-Retroid-Pocket-variant-Sig.patch b/patch/kernel/archive/sm8250-6.18/0041-arm64-dts-qcom-add-SM8250-Retroid-Pocket-variant-Sig.patch new file mode 100644 index 000000000000..b8e0b21a29c4 --- /dev/null +++ b/patch/kernel/archive/sm8250-6.18/0041-arm64-dts-qcom-add-SM8250-Retroid-Pocket-variant-Sig.patch @@ -0,0 +1,1666 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Mon, 25 Nov 2024 02:19:13 +0700 +Subject: arm64: dts: qcom: add SM8250 Retroid Pocket variant Signed-off-by: + Teguh Sobirin + +--- + arch/arm64/boot/dts/qcom/sm8250-retroidpocket-common.dtsi | 1435 ++++++++++ + arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rp5.dts | 100 + + arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rpmini.dts | 98 + + 3 files changed, 1633 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-common.dtsi b/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-common.dtsi +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-common.dtsi +@@ -0,0 +1,1435 @@ ++// SPDX-License-Identifier: BSD-3-Clause ++/* ++ * Copyright (c) 2024, Retroid Pocket. ++ */ ++ ++/dts-v1/; ++ ++#include ++#include ++#include ++#include ++#include ++#include "sm8250.dtsi" ++#include "pm8150.dtsi" ++#include "pm8150b.dtsi" ++#include "pm8150l.dtsi" ++ ++/ { ++ qcom,msm-id = <356 0x20001>; ++ qcom,board-id = <0x01001F 0x01>; ++ ++ aliases { ++ serial0 = &uart12; ++ serial1 = &uart6; ++ serial2 = &uart16; ++ sdhc2 = &sdhc_2; ++ }; ++ ++ battery: battery { ++ compatible = "simple-battery"; ++ ++ charge-full-design-microamp-hours = <3850000>; ++ voltage-min-design-microvolt = <3600000>; ++ voltage-max-design-microvolt = <4400000>; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ fan: pwm-fan { ++ compatible = "pwm-fan"; ++ cooling-levels = <0 32 64 128 255>; ++ #cooling-cells = <2>; ++ fan-supply = <&vreg_fan_pwr>; ++ pwms = <&pm8150l_lpg 3 100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&fan_pwm_active>; ++ }; ++ ++ gpio-keys { ++ compatible = "gpio-keys"; ++ ++ key-vol-up { ++ label = "Volume Up"; ++ linux,code = ; ++ gpios = <&pm8150_gpios 6 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ thermal-zones { ++ conn-thermal { ++ thermal-sensors = <&pm8150b_adc_tm 0>; ++ ++ trips { ++ active-config0 { ++ temperature = <125000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ pm8150l-pcb-thermal { ++ thermal-sensors = <&pm8150l_adc_tm 1>; ++ ++ trips { ++ active-config0 { ++ temperature = <50000>; ++ hysteresis = <4000>; ++ type = "passive"; ++ }; ++ }; ++ }; ++ ++ skin-msm-thermal { ++ thermal-sensors = <&pm8150l_adc_tm 0>; ++ ++ trips { ++ active-config0 { ++ temperature = <50000>; ++ hysteresis = <4000>; ++ type = "passive"; ++ }; ++ }; ++ }; ++ ++ wifi-thermal { ++ thermal-sensors = <&pm8150_adc_tm 1>; ++ ++ trips { ++ active-config0 { ++ temperature = <52000>; ++ hysteresis = <4000>; ++ type = "passive"; ++ }; ++ }; ++ }; ++ ++ xo-thermal { ++ thermal-sensors = <&pm8150_adc_tm 0>; ++ ++ trips { ++ active-config0 { ++ temperature = <50000>; ++ hysteresis = <4000>; ++ type = "passive"; ++ }; ++ }; ++ }; ++ }; ++ ++ vbat: vbat-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "VBAT"; ++ vin-supply = <&vreg_l11c_3p3>; ++ regulator-min-microvolt = <4200000>; ++ regulator-max-microvolt = <4200000>; ++ regulator-always-on; ++ }; ++ ++ vdc_3v3: vdc-3v3-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "VDC_3V3"; ++ vin-supply = <&vreg_l11c_3p3>; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ vdc_5v: vdc-5v-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "VDC_5V"; ++ ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ vin-supply = <&vreg_l11c_3p3>; ++ }; ++ ++ vdda_panel: vdda-panel-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vdda_panel"; ++ ++ regulator-min-microvolt = <2800000>; ++ regulator-max-microvolt = <2800000>; ++ ++ gpio = <&tlmm 4 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ regulator-boot-on; ++ }; ++ ++ vph_pwr: vph-pwr-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vph_pwr"; ++ regulator-min-microvolt = <3700000>; ++ regulator-max-microvolt = <3700000>; ++ regulator-always-on; ++ }; ++ ++ vreg_s4a_1p8: vreg-s4a-1p8 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vreg_s4a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ regulator-boot-on; ++ vin-supply = <&vph_pwr>; ++ }; ++ ++ vreg_fan_pwr: vreg-fan-pwr-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vreg_fan_pwr"; ++ ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ ++ gpio = <&tlmm 7 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vreg_mcu_3v3: vreg-mcu-3v3-regulator { ++ compatible = "regulator-fixed"; ++ regulator-name = "vreg_mcu_3v3"; ++ ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ ++ gpio = <&tlmm 3 GPIO_ACTIVE_HIGH>; ++ regulator-always-on; ++ regulator-boot-on; ++ enable-active-high; ++ }; ++ ++ qca6390-pmu { ++ compatible = "qcom,qca6390-pmu"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&bt_en_state>, <&wlan_en_state>; ++ ++ vddaon-supply = <&vreg_s6a_0p95>; ++ vddrfa1p3-supply = <&vreg_s8c_1p3>; ++ vddrfa1p9-supply = <&vreg_s5a_1p9>; ++ vddpcie1p3-supply = <&vreg_s8c_1p3>; ++ vddpcie1p9-supply = <&vreg_s5a_1p9>; ++ vddio-supply = <&vreg_s4a_1p8>; ++ ++ wlan-enable-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; ++ bt-enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; ++ ++ regulators { ++ vreg_pmu_rfa_cmn: ldo0 { ++ regulator-name = "vreg_pmu_rfa_cmn"; ++ }; ++ ++ vreg_pmu_aon_0p59: ldo1 { ++ regulator-name = "vreg_pmu_aon_0p59"; ++ }; ++ ++ vreg_pmu_wlcx_0p8: ldo2 { ++ regulator-name = "vreg_pmu_wlcx_0p8"; ++ }; ++ ++ vreg_pmu_wlmx_0p85: ldo3 { ++ regulator-name = "vreg_pmu_wlmx_0p85"; ++ }; ++ ++ vreg_pmu_btcmx_0p85: ldo4 { ++ regulator-name = "vreg_pmu_btcmx_0p85"; ++ }; ++ ++ vreg_pmu_rfa_0p8: ldo5 { ++ regulator-name = "vreg_pmu_rfa_0p8"; ++ }; ++ ++ vreg_pmu_rfa_1p2: ldo6 { ++ regulator-name = "vreg_pmu_rfa_1p2"; ++ }; ++ ++ vreg_pmu_rfa_1p7: ldo7 { ++ regulator-name = "vreg_pmu_rfa_1p7"; ++ }; ++ ++ vreg_pmu_pcie_0p9: ldo8 { ++ regulator-name = "vreg_pmu_pcie_0p9"; ++ }; ++ ++ vreg_pmu_pcie_1p8: ldo9 { ++ regulator-name = "vreg_pmu_pcie_1p8"; ++ }; ++ }; ++ }; ++ ++ wcd938x: audio-codec { ++ compatible = "qcom,wcd9385-codec"; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wcd_default>; ++ ++ reset-gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; ++ ++ vdd-buck-supply = <&vreg_s4a_1p8>; ++ vdd-rxtx-supply = <&vreg_s4a_1p8>; ++ vdd-io-supply = <&vreg_s4a_1p8>; ++ vdd-mic-bias-supply = <&vreg_bob>; ++ ++ qcom,micbias1-microvolt = <1800000>; ++ qcom,micbias2-microvolt = <1800000>; ++ qcom,micbias3-microvolt = <1800000>; ++ qcom,micbias4-microvolt = <1800000>; ++ qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; ++ qcom,mbhc-headset-vthreshold-microvolt = <1700000>; ++ qcom,mbhc-headphone-vthreshold-microvolt = <50000>; ++ qcom,rx-device = <&wcd_rx>; ++ qcom,tx-device = <&wcd_tx>; ++ ++ #sound-dai-cells = <1>; ++ }; ++ ++ reserved-memory { ++ cont_splash_mem: splash_region@9c000000 { ++ reg = <0x0 0x9c000000 0x0 0x2300000>; ++ no-map; ++ }; ++ }; ++}; ++ ++&adsp { ++ status = "okay"; ++ firmware-name = "qcom/sm8250/adsp.mbn"; ++}; ++ ++&apps_rsc { ++ regulators-0 { ++ compatible = "qcom,pm8150-rpmh-regulators"; ++ qcom,pmic-id = "a"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-s3-supply = <&vph_pwr>; ++ vdd-s4-supply = <&vph_pwr>; ++ vdd-s5-supply = <&vph_pwr>; ++ vdd-s6-supply = <&vph_pwr>; ++ vdd-s7-supply = <&vph_pwr>; ++ vdd-s8-supply = <&vph_pwr>; ++ vdd-s9-supply = <&vph_pwr>; ++ vdd-s10-supply = <&vph_pwr>; ++ vdd-l2-l10-supply = <&vreg_bob>; ++ vdd-l3-l4-l5-l18-supply = <&vreg_s6a_0p95>; ++ vdd-l6-l9-supply = <&vreg_s8c_1p3>; ++ vdd-l7-l12-l14-l15-supply = <&vreg_s5a_1p9>; ++ vdd-l13-l16-l17-supply = <&vreg_bob>; ++ ++ vreg_l2a_3p1: ldo2 { ++ regulator-name = "vreg_l2a_3p1"; ++ regulator-min-microvolt = <3072000>; ++ regulator-max-microvolt = <3072000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l3a_0p9: ldo3 { ++ regulator-name = "vreg_l3a_0p9"; ++ regulator-min-microvolt = <928000>; ++ regulator-max-microvolt = <932000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l5a_0p88: ldo5 { ++ regulator-name = "vreg_l5a_0p88"; ++ regulator-min-microvolt = <880000>; ++ regulator-max-microvolt = <880000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6a_1p2: ldo6 { ++ regulator-name = "vreg_l6a_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l7a_1p7: ldo7 { ++ regulator-name = "vreg_l7a_1p7"; ++ regulator-min-microvolt = <1704000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l9a_1p2: ldo9 { ++ regulator-name = "vreg_l9a_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l10a_1p8: ldo10 { ++ regulator-name = "vreg_l10a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l12a_1p8: ldo12 { ++ regulator-name = "vreg_l12a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l13a_ts_3p0: ldo13 { ++ regulator-name = "vreg_l13a_ts_3p0"; ++ regulator-min-microvolt = <3008000>; ++ regulator-max-microvolt = <3008000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l14a_1p8: ldo14 { ++ regulator-name = "vreg_l14a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1880000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l15a_1p8: ldo15 { ++ regulator-name = "vreg_l15a_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l16a_2p7: ldo16 { ++ regulator-name = "vreg_l16a_2p7"; ++ regulator-min-microvolt = <2704000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l17a_3p0: ldo17 { ++ regulator-name = "vreg_l17a_3p0"; ++ regulator-min-microvolt = <2856000>; ++ regulator-max-microvolt = <3008000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l18a_0p92: ldo18 { ++ regulator-name = "vreg_l18a_0p92"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <912000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_s5a_1p9: smps5 { ++ regulator-name = "vreg_s5a_1p9"; ++ regulator-min-microvolt = <1904000>; ++ regulator-max-microvolt = <2000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_s6a_0p95: smps6 { ++ regulator-name = "vreg_s6a_0p95"; ++ regulator-min-microvolt = <920000>; ++ regulator-max-microvolt = <1128000>; ++ regulator-initial-mode = ; ++ }; ++ }; ++ ++ regulators-1 { ++ compatible = "qcom,pm8150l-rpmh-regulators"; ++ qcom,pmic-id = "c"; ++ ++ vdd-s1-supply = <&vph_pwr>; ++ vdd-s2-supply = <&vph_pwr>; ++ vdd-s3-supply = <&vph_pwr>; ++ vdd-s4-supply = <&vph_pwr>; ++ vdd-s5-supply = <&vph_pwr>; ++ vdd-s6-supply = <&vph_pwr>; ++ vdd-s7-supply = <&vph_pwr>; ++ vdd-s8-supply = <&vph_pwr>; ++ vdd-l1-l8-supply = <&vreg_s4a_1p8>; ++ vdd-l2-l3-supply = <&vreg_s8c_1p3>; ++ vdd-l4-l5-l6-supply = <&vreg_bob>; ++ vdd-l7-l11-supply = <&vreg_bob>; ++ vdd-l9-l10-supply = <&vreg_bob>; ++ vdd-bob-supply = <&vph_pwr>; ++ ++ vreg_bob: bob { ++ regulator-name = "vreg_bob"; ++ regulator-min-microvolt = <3008000>; ++ regulator-max-microvolt = <4000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l1c_1p8: ldo1 { ++ regulator-name = "vreg_l1c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l2c_1p2: ldo2 { ++ regulator-name = "vreg_l2c_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l3c_1p2: ldo3 { ++ regulator-name = "vreg_l3c_1p2"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l4c_1p7: ldo4 { ++ regulator-name = "vreg_l4c_1p7"; ++ regulator-min-microvolt = <1704000>; ++ regulator-max-microvolt = <2928000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l5c_1p8: ldo5 { ++ regulator-name = "vreg_l5c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2928000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l6c_2p96: ldo6 { ++ regulator-name = "vreg_l6c_2p96"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l7c_cam_vcm0_2p85: ldo7 { ++ regulator-name = "vreg_l7c_cam_vcm0_2p85"; ++ regulator-min-microvolt = <2856000>; ++ regulator-max-microvolt = <3104000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l8c_1p8: ldo8 { ++ regulator-name = "vreg_l8c_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l9c_2p96: ldo9 { ++ regulator-name = "vreg_l9c_2p96"; ++ regulator-min-microvolt = <2704000>; ++ regulator-max-microvolt = <2960000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l10c_3p0: ldo10 { ++ regulator-name = "vreg_l10c_3p0"; ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-initial-mode = ; ++ }; ++ ++ vreg_l11c_3p3: ldo11 { ++ regulator-name = "vreg_l11c_3p3"; ++ regulator-min-microvolt = <3296000>; ++ regulator-max-microvolt = <3296000>; ++ regulator-initial-mode = ; ++ regulator-always-on; ++ }; ++ ++ vreg_s8c_1p3: smps8 { ++ regulator-name = "vreg_s8c_1p3"; ++ regulator-min-microvolt = <1352000>; ++ regulator-max-microvolt = <1352000>; ++ regulator-initial-mode = ; ++ }; ++ }; ++}; ++ ++&cdsp { ++ status = "okay"; ++ firmware-name = "qcom/sm8250/cdsp.mbn"; ++}; ++ ++&gmu { ++ status = "okay"; ++}; ++ ++&gpu { ++ status = "okay"; ++ ++ zap-shader { ++ memory-region = <&gpu_mem>; ++ firmware-name = "qcom/sm8250/a650_zap.mbn"; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ htr3212r: led-controller@3c { ++ compatible = "heroic,htr3212"; ++ reg = <0x3c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ sdb-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; ++ vdd-supply = <&vreg_mcu_3v3>; ++ ++ ledr_b1: led@1 { ++ reg = <1>; ++ label = "r:b1"; ++ color = ; ++ }; ++ ledr_g1: led@2 { ++ reg = <2>; ++ label = "r:g1"; ++ color = ; ++ }; ++ ledr_r1: led@3 { ++ reg = <3>; ++ label = "r:r1"; ++ color = ; ++ }; ++ ledr_b2: led@4 { ++ reg = <4>; ++ label = "r:b2"; ++ color = ; ++ }; ++ ledr_g2: led@5 { ++ reg = <5>; ++ label = "r:g2"; ++ color = ; ++ }; ++ ledr_r2: led@6 { ++ reg = <6>; ++ label = "r:r2"; ++ color = ; ++ }; ++ ledr_b3: led@7 { ++ reg = <7>; ++ label = "r:b3"; ++ color = ; ++ }; ++ ledr_g3: led@8 { ++ reg = <8>; ++ label = "r:g3"; ++ color = ; ++ }; ++ ledr_r3: led@9 { ++ reg = <9>; ++ label = "r:r3"; ++ color = ; ++ }; ++ ledr_b4: led@10 { ++ reg = <10>; ++ label = "r:b4"; ++ color = ; ++ }; ++ ledr_g4: led@11 { ++ reg = <11>; ++ label = "r:g4"; ++ color = ; ++ }; ++ ledr_r4: led@12 { ++ reg = <12>; ++ label = "r:r4"; ++ color = ; ++ }; ++ }; ++}; ++ ++&i2c5 { ++ clock-frequency = <400000>; ++ status = "okay"; ++ ++ htr3212l: led-controller@3c { ++ compatible = "heroic,htr3212"; ++ reg = <0x3c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ sdb-gpios = <&tlmm 5 GPIO_ACTIVE_HIGH>; ++ vdd-supply = <&vreg_mcu_3v3>; ++ ++ ledl_b1: led@1 { ++ reg = <1>; ++ label = "l:b1"; ++ color = ; ++ }; ++ ledl_g1: led@2 { ++ reg = <2>; ++ label = "l:g1"; ++ color = ; ++ }; ++ ledl_r1: led@3 { ++ reg = <3>; ++ label = "l:r1"; ++ color = ; ++ }; ++ ledl_b2: led@4 { ++ reg = <4>; ++ label = "l:b2"; ++ color = ; ++ }; ++ ledl_g2: led@5 { ++ reg = <5>; ++ label = "l:g2"; ++ color = ; ++ }; ++ ledl_r2: led@6 { ++ reg = <6>; ++ label = "l:r2"; ++ color = ; ++ }; ++ ledl_b3: led@7 { ++ reg = <7>; ++ label = "l:b3"; ++ color = ; ++ }; ++ ledl_g3: led@8 { ++ reg = <8>; ++ label = "l:g3"; ++ color = ; ++ }; ++ ledl_r3: led@9 { ++ reg = <9>; ++ label = "l:r3"; ++ color = ; ++ }; ++ ledl_b4: led@10 { ++ reg = <10>; ++ label = "l:b4"; ++ color = ; ++ }; ++ ledl_g4: led@11 { ++ reg = <11>; ++ label = "l:g4"; ++ color = ; ++ }; ++ ledl_r4: led@12 { ++ reg = <12>; ++ label = "l:r4"; ++ color = ; ++ }; ++ }; ++}; ++ ++&i2c13 { ++ status = "okay"; ++ clock-frequency = <400000>; ++ ++ touchscreen@38 { ++ compatible = "focaltech,ft5452"; ++ reg = <0x38>; ++ ++ interrupt-parent = <&tlmm>; ++ interrupts = <39 IRQ_TYPE_EDGE_FALLING>; ++ reset-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; ++ ++ vcc-supply = <&vreg_l13a_ts_3p0>; ++ iovcc-supply = <&vreg_l1c_1p8>; ++ ++ pinctrl-0 = <&ts_int_active &ts_reset_active>; ++ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; ++ pinctrl-names = "default", "sleep"; ++ ++ touchscreen-size-x = <960>; ++ touchscreen-size-y = <1280>; ++ }; ++}; ++ ++&i2c15 { ++ status = "okay"; ++ ++ typec-mux@1c { ++ compatible = "onnn,nb7vpq904m"; ++ reg = <0x1c>; ++ ++ enable-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>; ++ ++ vcc-supply = <&vreg_s4a_1p8>; ++ ++ retimer-switch; ++ orientation-switch; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ ++ redriver_usb_con_ss: endpoint { ++ remote-endpoint = <&pm8150b_typec_mux_in>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ redriver_phy_con_ss: endpoint { ++ remote-endpoint = <&usb_1_qmpphy_out>; ++ data-lanes = <0 1 2 3>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ ++ redriver_usb_con_sbu: endpoint { ++ remote-endpoint = <&pm8150b_typec_sbu_out>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&mdss { ++ status = "okay"; ++}; ++ ++&mdss_dp { ++ status = "okay"; ++}; ++ ++&mdss_dp_out { ++ data-lanes = <0 1>; ++ remote-endpoint = <&usb_1_qmpphy_dp_in>; ++}; ++ ++&mdss_dsi0 { ++ status = "okay"; ++ vdda-supply = <&vreg_l9a_1p2>; ++ ++ panel@0 { ++ reg = <0>; ++ ++ reset-gpios = <&tlmm 75 GPIO_ACTIVE_HIGH>; ++ ++ vdd1v2-supply = <&vreg_l3c_1p2>; ++ vddio-supply = <&vreg_l14a_1p8>; ++ vdd-supply = <&vreg_l11c_3p3>; ++ avdd-supply = <&vdda_panel>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ ++ panel_in_0: endpoint { ++ remote-endpoint = <&mdss_dsi0_out>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&mdss_dsi0_out { ++ data-lanes = <0 1 2 3>; ++ remote-endpoint = <&panel_in_0>; ++}; ++ ++&mdss_dsi0_phy { ++ status = "okay"; ++ vdds-supply = <&vreg_l5a_0p88>; ++}; ++ ++&pcie0 { ++ status = "okay"; ++}; ++ ++&pcie0_phy { ++ status = "okay"; ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++}; ++ ++&pcieport0 { ++ wifi@0 { ++ compatible = "pci17cb,1101"; ++ reg = <0x10000 0x0 0x0 0x0 0x0>; ++ ++ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; ++ vddaon-supply = <&vreg_pmu_aon_0p59>; ++ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; ++ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; ++ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; ++ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; ++ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; ++ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; ++ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; ++ }; ++}; ++ ++&pm8150_adc { ++ channel@4c { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "xo_therm"; ++ }; ++ ++ channel@4e { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "wifi_therm"; ++ }; ++}; ++ ++&pm8150_adc_tm { ++ status = "okay"; ++ ++ xo-therm@0 { ++ reg = <0>; ++ io-channels = <&pm8150_adc ADC5_XO_THERM_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++ ++ wifi-therm@1 { ++ reg = <1>; ++ io-channels = <&pm8150_adc ADC5_AMUX_THM2_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++}; ++ ++&pm8150b_adc { ++ channel@4f { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "conn_therm"; ++ }; ++}; ++ ++&pm8150b_adc_tm { ++ status = "okay"; ++ ++ conn-therm@0 { ++ reg = <0>; ++ io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++}; ++ ++&pm8150b_charger { ++ monitored-battery = <&battery>; ++ status = "okay"; ++}; ++ ++&pm8150b_fg { ++ status = "okay"; ++ monitored-battery = <&battery>; ++ power-supplies = <&pm8150b_charger>; ++}; ++ ++&pm8150b_haptics { ++ status = "okay"; ++ qcom,boost-gpios = <&pm8150b_gpios 5 GPIO_ACTIVE_HIGH>; ++ qcom,wave-play-rate-us = <5882>; ++ qcom,actuator-type = ; ++}; ++ ++&pm8150b_typec { ++ status = "okay"; ++ ++ vdd-pdphy-supply = <&vreg_l2a_3p1>; ++ ++ connector { ++ compatible = "usb-c-connector"; ++ ++ power-role = "dual"; ++ data-role = "dual"; ++ try-power-role = "sink"; ++ self-powered; ++ ++ source-pdos = ; ++ ++ sink-pdos = ; ++ ++ op-sink-microwatt = <15000000>; ++ ++ altmodes { ++ displayport { ++ svid = /bits/ 16 <0xff01>; ++ vdo = <0x00001c46>; ++ }; ++ }; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ pm8150b_hs_in: endpoint { ++ remote-endpoint = <&usb_1_dwc3_hs_out>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ pm8150b_typec_mux_in: endpoint { ++ remote-endpoint = <&redriver_usb_con_ss>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ ++ pm8150b_typec_sbu_out: endpoint { ++ remote-endpoint = <&redriver_usb_con_sbu>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&pm8150b_vbus { ++ regulator-min-microamp = <500000>; ++ regulator-max-microamp = <3000000>; ++ status = "okay"; ++}; ++ ++&pm8150l_adc { ++ channel@4e { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "skin_msm_therm"; ++ }; ++ ++ channel@4f { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ label = "pm8150l_therm"; ++ }; ++}; ++ ++&pm8150l_adc_tm { ++ status = "okay"; ++ ++ skin-msm-therm@0 { ++ reg = <0>; ++ io-channels = <&pm8150l_adc ADC5_AMUX_THM2_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++ ++ pm8150l-therm@1 { ++ reg = <1>; ++ io-channels = <&pm8150l_adc ADC5_AMUX_THM3_100K_PU>; ++ qcom,ratiometric; ++ qcom,hw-settle-time-us = <200>; ++ }; ++}; ++ ++&pm8150l_gpios { ++ fan_pwm_active: fan-pwm-active-state { ++ pins = "gpio6"; ++ function = "func1"; ++ bias-disable; ++ power-source = <0>; ++ output-low; ++ qcom,drive-strength = <3>; ++ drive-push-pull; ++ }; ++}; ++ ++&pm8150l_lpg { ++ status = "okay"; ++}; ++ ++&pon_pwrkey { ++ status = "okay"; ++}; ++ ++&pon_resin { ++ status = "okay"; ++ ++ linux,code = ; ++}; ++ ++&q6asmdai { ++ dai@0 { ++ reg = <0>; ++ }; ++ ++ dai@1 { ++ reg = <1>; ++ }; ++ ++ dai@2 { ++ reg = <2>; ++ }; ++}; ++ ++&qupv3_id_0 { ++ status = "okay"; ++}; ++ ++&qupv3_id_1 { ++ status = "okay"; ++}; ++ ++&qupv3_id_2 { ++ status = "okay"; ++}; ++ ++&qup_uart16_default { ++ drive-strength = <16>; ++ bias-pull-up; ++}; ++ ++&rxmacro { ++ status = "okay"; ++}; ++ ++&sdhc_2 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdc2_default_state &sdc2_card_det_n>; ++ vmmc-supply = <&vreg_l9c_2p96>; ++ vqmmc-supply = <&vreg_l6c_2p96>; ++ cd-gpios = <&tlmm 77 GPIO_ACTIVE_LOW>; ++ bus-width = <4>; ++ no-sdio; ++ no-mmc; ++}; ++ ++&sound { ++ compatible = "qcom,sm8250-sndcard"; ++ model = "RetroidPocket"; ++ audio-routing = ++ "SpkrLeft IN", "WSA_SPK1 OUT", ++ "SpkrRight IN", "WSA_SPK2 OUT", ++ "IN1_HPHL", "HPHL_OUT", ++ "IN2_HPHR", "HPHR_OUT", ++ "MM_DL1", "MultiMedia1 Playback", ++ "MM_DL2", "MultiMedia2 Playback", ++ "MultiMedia3 Capture", "MM_UL3"; ++ ++ mm1-dai-link { ++ link-name = "MultiMedia1"; ++ cpu { ++ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; ++ }; ++ }; ++ ++ mm2-dai-link { ++ link-name = "MultiMedia2"; ++ cpu { ++ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; ++ }; ++ }; ++ ++ mm3-dai-link { ++ link-name = "MultiMedia3"; ++ cpu { ++ sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA3>; ++ }; ++ }; ++ ++ wcd-playback-dai-link { ++ link-name = "WCD Playback"; ++ cpu { ++ sound-dai = <&q6afedai RX_CODEC_DMA_RX_0>; ++ }; ++ codec { ++ sound-dai = <&wcd938x 0>, <&swr1 0>, <&rxmacro 0>; ++ }; ++ platform { ++ sound-dai = <&q6routing>; ++ }; ++ }; ++ ++ wsa-dai-link { ++ link-name = "WSA Playback"; ++ cpu { ++ sound-dai = <&q6afedai WSA_CODEC_DMA_RX_0>; ++ }; ++ ++ codec { ++ sound-dai = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&wsamacro 0>; ++ }; ++ platform { ++ sound-dai = <&q6routing>; ++ }; ++ }; ++ ++ wcd-capture-dai-link { ++ link-name = "WCD Capture"; ++ cpu { ++ sound-dai = <&q6afedai TX_CODEC_DMA_TX_3>; ++ }; ++ ++ codec { ++ sound-dai = <&wcd938x 1>, <&swr2 0>, <&txmacro 0>; ++ }; ++ platform { ++ sound-dai = <&q6routing>; ++ }; ++ }; ++}; ++ ++&swr0 { ++ status = "okay"; ++ ++ left_spkr: speaker@0,1 { ++ compatible = "sdw10217201000"; ++ reg = <0 1>; ++ powerdown-gpios = <&tlmm 129 GPIO_ACTIVE_LOW>; ++ #thermal-sensor-cells = <0>; ++ sound-name-prefix = "SpkrLeft"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ right_spkr: speaker@0,2 { ++ compatible = "sdw10217201000"; ++ reg = <0 2>; ++ powerdown-gpios = <&tlmm 127 GPIO_ACTIVE_LOW>; ++ #thermal-sensor-cells = <0>; ++ sound-name-prefix = "SpkrRight"; ++ #sound-dai-cells = <0>; ++ }; ++}; ++ ++&swr1 { ++ status = "okay"; ++ ++ wcd_rx: wcd9385-rx@0,4 { ++ compatible = "sdw20217010d00"; ++ reg = <0 4>; ++ qcom,rx-port-mapping = <1 2 3 4 5>; ++ }; ++}; ++ ++&swr2 { ++ status = "okay"; ++ ++ wcd_tx: wcd9385-tx@0,3 { ++ compatible = "sdw20217010d00"; ++ reg = <0 3>; ++ qcom,tx-port-mapping = <2 3 4 5>; ++ }; ++}; ++ ++&tlmm { ++ bt_en_state: bt-default-state { ++ pins = "gpio21"; ++ function = "gpio"; ++ drive-strength = <16>; ++ output-low; ++ bias-pull-up; ++ }; ++ ++ mcu_boot_default: mcu-boot-default-state { ++ pins = "gpio110"; ++ function = "gpio"; ++ bias-pull-down; ++ }; ++ ++ mcu_en_default: mcu-en-default-state { ++ pins = "gpio111"; ++ function = "gpio"; ++ bias-pull-down; ++ }; ++ ++ mcu_rst_default: mcu-rst-default-state { ++ pins = "gpio109"; ++ function = "gpio"; ++ bias-pull-down; ++ }; ++ ++ sdc2_default_state: sdc2-default-state { ++ clk-pins { ++ pins = "sdc2_clk"; ++ bias-disable; ++ drive-strength = <16>; ++ }; ++ ++ cmd-pins { ++ pins = "sdc2_cmd"; ++ bias-pull-up; ++ drive-strength = <10>; ++ }; ++ ++ data-pins { ++ pins = "sdc2_data"; ++ bias-pull-up; ++ drive-strength = <10>; ++ }; ++ }; ++ ++ sdc2_card_det_n: sd-card-det-n-state { ++ pins = "gpio77"; ++ function = "gpio"; ++ bias-pull-up; ++ }; ++ ++ ts_int_active: ts-int-active-state { ++ pins = "gpio39"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-pull-up; ++ }; ++ ++ ts_int_suspend: ts-int-suspend-state { ++ pins = "gpio39"; ++ function = "gpio"; ++ drive-strength = <2>; ++ bias-pull-down; ++ }; ++ ++ ts_reset_active: ts-reset-active-state { ++ pins = "gpio38"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-pull-up; ++ }; ++ ++ ts_reset_suspend: ts-reset-suspend-state { ++ pins = "gpio38"; ++ function = "gpio"; ++ drive-strength = <2>; ++ bias-pull-down; ++ }; ++ ++ wcd_default: wcd-default-state { ++ reset-pins { ++ pins = "gpio32"; ++ function = "gpio"; ++ bias-disable; ++ }; ++ }; ++ ++ wlan_en_state: wlan-default-state { ++ pins = "gpio20"; ++ function = "gpio"; ++ drive-strength = <16>; ++ output-low; ++ bias-pull-up; ++ }; ++}; ++ ++&txmacro { ++ status = "okay"; ++}; ++ ++&uart6 { ++ status = "okay"; ++ ++ bluetooth { ++ compatible = "qcom,qca6390-bt"; ++ ++ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; ++ vddaon-supply = <&vreg_pmu_aon_0p59>; ++ vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; ++ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; ++ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; ++ vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; ++ }; ++}; ++ ++&uart12 { ++ status = "okay"; ++}; ++ ++&uart16 { ++ status = "okay"; ++ ++ gamepad { ++ compatible = "ayn,odin2-gamepad"; ++ ++ gamepad-name = "Retroid Pocket Controller"; ++ gamepad-bus = <0x0003>; ++ gamepad-vid = <0x2020>; ++ gamepad-pid = <0x3001>; ++ gamepad-rev = <0x0001>; ++ ++ boot-gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>; ++ enable-gpios = <&tlmm 111 GPIO_ACTIVE_HIGH>; ++ reset-gpios = <&tlmm 109 GPIO_ACTIVE_HIGH>; ++ pinctrl-0 = <&mcu_boot_default &mcu_en_default &mcu_rst_default>; ++ pinctrl-names = "default"; ++ }; ++}; ++ ++&ufs_mem_hc { ++ status = "okay"; ++ ++ vcc-supply = <&vreg_l17a_3p0>; ++ vcc-max-microamp = <800000>; ++ vccq-supply = <&vreg_l6a_1p2>; ++ vccq-max-microamp = <800000>; ++ vccq2-supply = <&vreg_s4a_1p8>; ++ vccq2-max-microamp = <800000>; ++}; ++ ++&ufs_mem_phy { ++ status = "okay"; ++ ++ vdda-phy-supply = <&vreg_l5a_0p88>; ++ vdda-pll-supply = <&vreg_l9a_1p2>; ++}; ++ ++&usb_1 { ++ status = "okay"; ++}; ++ ++&usb_1_dwc3 { ++ dr_mode = "otg"; ++ usb-role-switch; ++}; ++ ++&usb_1_dwc3_hs_out { ++ remote-endpoint = <&pm8150b_hs_in>; ++}; ++ ++&usb_1_hsphy { ++ status = "okay"; ++ ++ vdda-pll-supply = <&vreg_l5a_0p88>; ++ vdda33-supply = <&vreg_l2a_3p1>; ++ vdda18-supply = <&vreg_l12a_1p8>; ++}; ++ ++&usb_1_qmpphy { ++ status = "okay"; ++ ++ vdda-phy-supply = <&vreg_l9a_1p2>; ++ vdda-pll-supply = <&vreg_l18a_0p92>; ++}; ++ ++&usb_1_qmpphy_dp_in { ++ remote-endpoint = <&mdss_dp_out>; ++}; ++ ++&usb_1_qmpphy_out { ++ remote-endpoint = <&redriver_phy_con_ss>; ++}; ++ ++&venus { ++ status = "okay"; ++}; ++ ++&wsamacro { ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rp5.dts b/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rp5.dts +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rp5.dts +@@ -0,0 +1,100 @@ ++// SPDX-License-Identifier: BSD-3-Clause ++/* ++ * Copyright (c) 2024, Retroid Pocket. ++ */ ++ ++/dts-v1/; ++ ++#include "sm8250-retroidpocket-common.dtsi" ++ ++/ { ++ model = "Retroid Pocket 5"; ++ compatible = "retroidpocket,rp5", "qcom,sm8250"; ++ ++ chosen { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ framebuffer: framebuffer@9c000000 { ++ compatible = "simple-framebuffer"; ++ reg = <0x0 0x9c000000 0x0 0x2300000>; ++ width = <1080>; ++ height = <1920>; ++ stride = <(1080 * 4)>; ++ format = "a8r8g8b8"; ++ }; ++ }; ++ ++ multi-ledr1 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r1"; ++ leds = <&ledr_r4>, <&ledr_g4>, <&ledr_b4>; ++ }; ++ ++ multi-ledl1 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l1"; ++ leds = <&ledl_r4>, <&ledl_g4>, <&ledl_b4>; ++ }; ++ ++ multi-ledr2 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r2"; ++ leds = <&ledr_r1>, <&ledr_g1>, <&ledr_b1>; ++ }; ++ ++ multi-ledl2 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l2"; ++ leds = <&ledl_r1>, <&ledl_g1>, <&ledl_b1>; ++ }; ++ ++ multi-ledr3 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r3"; ++ leds = <&ledr_r2>, <&ledr_g2>, <&ledr_b2>; ++ }; ++ ++ multi-ledl3 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l3"; ++ leds = <&ledl_r2>, <&ledl_g2>, <&ledl_b2>; ++ }; ++ ++ multi-ledr4 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r4"; ++ leds = <&ledr_r3>, <&ledr_g3>, <&ledr_b3>; ++ }; ++ ++ multi-ledl4 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l4"; ++ leds = <&ledl_r3>, <&ledl_g3>, <&ledl_b3>; ++ }; ++}; ++ ++&i2c13 { ++ touchscreen@38 { ++ touchscreen-size-x = <1080>; ++ touchscreen-size-y = <1920>; ++ touchscreen-inverted-x; ++ touchscreen-inverted-y; ++ }; ++}; ++ ++&mdss_dsi0 { ++ panel@0 { ++ compatible = "ch13726a,rp5"; ++ rotation = <270>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rpmini.dts b/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rpmini.dts +new file mode 100644 +index 000000000000..111111111111 +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/sm8250-retroidpocket-rpmini.dts +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: BSD-3-Clause ++/* ++ * Copyright (c) 2024, Retroid Pocket. ++ */ ++ ++/dts-v1/; ++ ++#include "sm8250-retroidpocket-common.dtsi" ++ ++/ { ++ model = "Retroid Pocket Mini"; ++ compatible = "retroidpocket,rpmini", "qcom,sm8250"; ++ ++ chosen { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ framebuffer: framebuffer@9c000000 { ++ compatible = "simple-framebuffer"; ++ reg = <0x0 0x9c000000 0x0 0x2300000>; ++ width = <960>; ++ height = <1280>; ++ stride = <(960 * 4)>; ++ format = "a8r8g8b8"; ++ }; ++ }; ++ ++ multi-ledr1 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r1"; ++ leds = <&ledr_r1>, <&ledr_g1>, <&ledr_b1>; ++ }; ++ ++ multi-ledl1 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l1"; ++ leds = <&ledl_r1>, <&ledl_g1>, <&ledl_b1>; ++ }; ++ ++ multi-ledr2 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r2"; ++ leds = <&ledr_r2>, <&ledr_g2>, <&ledr_b2>; ++ }; ++ ++ multi-ledl2 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l2"; ++ leds = <&ledl_r2>, <&ledl_g2>, <&ledl_b2>; ++ }; ++ ++ multi-ledr3 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r3"; ++ leds = <&ledr_r3>, <&ledr_g3>, <&ledr_b3>; ++ }; ++ ++ multi-ledl3 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l3"; ++ leds = <&ledl_r3>, <&ledl_g3>, <&ledl_b3>; ++ }; ++ ++ multi-ledr4 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "r4"; ++ leds = <&ledr_r4>, <&ledr_g4>, <&ledr_b4>; ++ }; ++ ++ multi-ledl4 { ++ compatible = "leds-group-multicolor"; ++ color = ; ++ function = "l4"; ++ leds = <&ledl_r4>, <&ledl_g4>, <&ledl_b4>; ++ }; ++}; ++ ++&i2c13 { ++ touchscreen@38 { ++ touchscreen-size-x = <960>; ++ touchscreen-size-y = <1280>; ++ }; ++}; ++ ++&mdss_dsi0 { ++ panel@0 { ++ compatible = "ch13726a,rpmini"; ++ rotation = <90>; ++ }; ++}; +-- +Armbian +