diff --git a/config/sources/families/sm8550.conf b/config/sources/families/sm8550.conf index 345174165716..eff948f8b5d0 100644 --- a/config/sources/families/sm8550.conf +++ b/config/sources/families/sm8550.conf @@ -22,8 +22,8 @@ case $BRANCH in ;; 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 KERNELBRANCH='branch:linux-6.19.y' declare -g -i KERNEL_GIT_CACHE_TTL=120 # 2 minutes; this is a high-traffic repo ;; diff --git a/patch/kernel/archive/sm8550-6.19/0001-arm64-dts-qcom-sm8550-add-UART15.patch b/patch/kernel/archive/sm8550-6.19/0001-arm64-dts-qcom-sm8550-add-UART15.patch new file mode 100644 index 000000000000..3523f461591b --- /dev/null +++ b/patch/kernel/archive/sm8550-6.19/0001-arm64-dts-qcom-sm8550-add-UART15.patch @@ -0,0 +1,53 @@ +From 0bb546348037195d1c67e5785cf39d5ea05b0ef6 Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Wed, 12 Feb 2025 17:53:48 +0800 +Subject: [PATCH 01/15] arm64: dts: qcom: sm8550: add UART15 + +Signed-off-by: Teguh Sobirin +--- + arch/arm64/boot/dts/qcom/sm8550.dtsi | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi +index e3f93f4f412d..9b5db1ba451c 100644 +--- a/arch/arm64/boot/dts/qcom/sm8550.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi +@@ -1251,6 +1251,20 @@ &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>, + #size-cells = <0>; + status = "disabled"; + }; ++ ++ uart15: serial@89c000 { ++ compatible = "qcom,geni-uart"; ++ reg = <0 0x89c000 0 0x4000>; ++ clock-names = "se"; ++ clocks = <&gcc GCC_QUPV3_WRAP2_S7_CLK>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&qup_uart15_default>; ++ interrupts = ; ++ interconnects = <&clk_virt MASTER_QUP_CORE_2 0 &clk_virt SLAVE_QUP_CORE_2 0>, ++ <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_QUP_2 0>; ++ interconnect-names = "qup-core", "qup-config"; ++ status = "disabled"; ++ }; + }; + + i2c_master_hub_0: geniqup@9c0000 { +@@ -5095,6 +5109,14 @@ qup_uart14_cts_rts: qup-uart14-cts-rts-state { + bias-pull-down; + }; + ++ qup_uart15_default: qup-uart15-default-state { ++ /* TX, RX */ ++ pins = "gpio74", "gpio75"; ++ function = "qup2_se7"; ++ drive-strength = <2>; ++ bias-pull-up; ++ }; ++ + sdc2_sleep: sdc2-sleep-state { + clk-pins { + pins = "sdc2_clk"; +-- +2.43.0 + diff --git a/patch/kernel/archive/sm8550-6.19/0002-input-Add-driver-for-RSInput-Gamepad.patch b/patch/kernel/archive/sm8550-6.19/0002-input-Add-driver-for-RSInput-Gamepad.patch new file mode 100644 index 000000000000..6d26405d5ac5 --- /dev/null +++ b/patch/kernel/archive/sm8550-6.19/0002-input-Add-driver-for-RSInput-Gamepad.patch @@ -0,0 +1,472 @@ +From 8461053a3b7351028b98910cb4ec0e2c7f53e85b Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Thu, 20 Feb 2025 14:50:30 +0800 +Subject: [PATCH 02/15] input: Add driver for RSInput Gamepad + +Signed-off-by: Teguh Sobirin +--- + drivers/input/joystick/Kconfig | 4 + + drivers/input/joystick/Makefile | 1 + + drivers/input/joystick/rsinput.c | 423 +++++++++++++++++++++++++++++++ + 3 files changed, 428 insertions(+) + create mode 100644 drivers/input/joystick/rsinput.c + +diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig +index 7755e5b454d2..0da3c0f44ecf 100644 +--- a/drivers/input/joystick/Kconfig ++++ b/drivers/input/joystick/Kconfig +@@ -383,6 +383,10 @@ config JOYSTICK_QWIIC + To compile this driver as a module, choose M here: the + module will be called qwiic-joystick. + ++config JOYSTICK_RSINPUT ++ tristate "UART Based gamepad driver that found in AYN and Retroid Pocket products" ++ depends on SERIAL_DEV_BUS ++ + config JOYSTICK_FSIA6B + tristate "FlySky FS-iA6B RC Receiver" + select SERIO +diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile +index 9976f596a920..3de503e29489 100644 +--- a/drivers/input/joystick/Makefile ++++ b/drivers/input/joystick/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_JOYSTICK_N64) += n64joy.o + obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o + obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o + obj-$(CONFIG_JOYSTICK_QWIIC) += qwiic-joystick.o ++obj-$(CONFIG_JOYSTICK_RSINPUT) += rsinput.o + obj-$(CONFIG_JOYSTICK_SEESAW) += adafruit-seesaw.o + obj-$(CONFIG_JOYSTICK_SENSEHAT) += sensehat-joystick.o + obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o +diff --git a/drivers/input/joystick/rsinput.c b/drivers/input/joystick/rsinput.c +new file mode 100644 +index 000000000000..8fc0c6ac3745 +--- /dev/null ++++ b/drivers/input/joystick/rsinput.c +@@ -0,0 +1,423 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * RSInput Gamepad Driver ++ * ++ * Copyright (C) 2024 Teguh Sobirin ++ * ++ */ ++#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FRAME_HEAD_1 0xA5 ++#define FRAME_HEAD_2 0xD3 ++#define FRAME_HEAD_3 0x5A ++#define FRAME_HEAD_4 0x3D ++ ++#define CMD_COMMOD 0x01 ++#define CMD_STATUS 0x02 ++ ++#define DATA_COMMOD_VERSION 0x02 ++#define DATA_COMMOD_SET_PAR 0x05 ++ ++#define FRAME_POS_SEQ 4 ++#define FRAME_POS_CMD 5 ++#define FRAME_POS_LEN_L 6 ++#define FRAME_POS_LEN_H 7 ++#define FRAME_POS_DATA_1 8 ++#define FRAME_POS_DATA_2 9 ++#define FRAME_POS_DATA_3 10 ++#define FRAME_POS_DATA_4 11 ++#define FRAME_POS_DATA_5 12 ++#define FRAME_POS_DATA_6 13 ++#define FRAME_POS_DATA_7 14 ++#define FRAME_POS_DATA_8 15 ++#define FRAME_POS_DATA_9 16 ++#define FRAME_POS_DATA_10 17 ++#define FRAME_POS_DATA_11 18 ++#define FRAME_POS_DATA_12 19 ++#define FRAME_POS_DATA_13 20 ++#define FRAME_POS_DATA_14 21 ++ ++#define MCU_PKT_SIZE_MIN 9 ++ ++#define MCU_VERSION_MAX_LEN 64 ++ ++struct rsinput_driver { ++ struct serdev_device *serdev; ++ struct input_dev *input; ++ struct gpio_desc *boot_gpio; ++ struct gpio_desc *enable_gpio; ++ struct gpio_desc *reset_gpio; ++ uint8_t rx_buf[256]; ++ uint8_t sequence_number; ++}; ++ ++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 ++}; ++ ++static uint8_t compute_checksum(const uint8_t *data, size_t len) { ++ uint8_t checksum = 0; ++ ++ for (size_t i = FRAME_POS_SEQ; i < len - 1; i++) { ++ checksum ^= data[i]; ++ } ++ ++ return checksum; ++} ++ ++static int rsinput_send_command(struct rsinput_driver *drv, uint8_t cmd, const uint8_t *data, size_t len) { ++ uint8_t frame[256]; ++ uint8_t checksum = 0; ++ size_t frame_len = 0; ++ ++ frame[frame_len++] = FRAME_HEAD_1; ++ frame[frame_len++] = FRAME_HEAD_2; ++ frame[frame_len++] = FRAME_HEAD_3; ++ frame[frame_len++] = FRAME_HEAD_4; ++ ++ frame[frame_len++] = drv->sequence_number; ++ drv->sequence_number++; ++ ++ frame[frame_len++] = cmd; ++ ++ frame[frame_len++] = len & 0xFF; ++ frame[frame_len++] = (len >> 8) & 0xFF; ++ ++ if (data && len) { ++ memcpy(&frame[frame_len], data, len); ++ frame_len += len; ++ } ++ ++ checksum = compute_checksum(frame, frame_len + 1); ++ frame[frame_len++] = checksum; ++ ++ return serdev_device_write_buf(drv->serdev, frame, frame_len); ++} ++ ++static int rsinput_init_commands(struct rsinput_driver *drv) { ++ int error; ++ ++ msleep(100); ++ uint8_t version_request[] = {DATA_COMMOD_VERSION}; ++ error = rsinput_send_command(drv, CMD_COMMOD, version_request, sizeof(version_request)); ++ if (error < 0) { ++ dev_err(&drv->serdev->dev, "Failed to request MCU version: %d\n", error); ++ return error; ++ } ++ ++ msleep(100); ++ uint8_t mcu_params[] = { ++ DATA_COMMOD_SET_PAR, ++ 0x01, ++ 0x00, 0x00, 0x00, 0x28, ++ 0x00, 0x00, 0x00, 0x07 ++ }; ++ error = rsinput_send_command(drv, CMD_COMMOD, mcu_params, sizeof(mcu_params)); ++ if (error < 0) { ++ dev_err(&drv->serdev->dev, "Failed to set MCU parameters: %d\n", error); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static void handle_cmd_commod(struct rsinput_driver *drv, const uint8_t *data, size_t payload_length) { ++ switch (data[FRAME_POS_DATA_1]) { ++ case DATA_COMMOD_VERSION: ++ if (payload_length >= 1) { ++ char mcu_version[MCU_VERSION_MAX_LEN] = {0}; ++ size_t version_length = payload_length; ++ if (version_length > MCU_VERSION_MAX_LEN - 1) { ++ version_length = MCU_VERSION_MAX_LEN - 1; ++ } ++ memcpy(mcu_version, &data[FRAME_POS_DATA_1], version_length); ++ mcu_version[version_length] = '\0'; ++ dev_info(&drv->serdev->dev, "MCU Version: %s\n", mcu_version); ++ } else { ++ dev_err(&drv->serdev->dev, "Invalid MCU version response length\n"); ++ } ++ break; ++ case DATA_COMMOD_SET_PAR: ++ dev_info(&drv->serdev->dev, "MCU parameters set successfully\n"); ++ break; ++ default: ++ dev_warn(&drv->serdev->dev, "Unhandled CMD_COMMOD sub-command: 0x%02x\n", data[FRAME_POS_DATA_1]); ++ break; ++ } ++} ++ ++static void handle_cmd_status(struct rsinput_driver *drv, const uint8_t *data, size_t payload_length) { ++ if (payload_length >= 6) { ++ static unsigned long prev_states; ++ unsigned long keys = data[FRAME_POS_DATA_1] | (data[FRAME_POS_DATA_2] << 8); ++ unsigned long current_states = keys, changes; ++ int i; ++ ++ bitmap_xor(&changes, ¤t_states, &prev_states, ARRAY_SIZE(keymap)); ++ ++ for_each_set_bit(i, &changes, ARRAY_SIZE(keymap)) { ++ input_report_key(drv->input, keymap[i], (current_states & BIT(i))); ++ } ++ ++ input_report_abs(drv->input, ABS_HAT2X, ++ 0x650 - (data[FRAME_POS_DATA_3] | (data[FRAME_POS_DATA_4] << 8))); ++ input_report_abs(drv->input, ABS_HAT2Y, ++ 0x650 - (data[FRAME_POS_DATA_5] | (data[FRAME_POS_DATA_6] << 8))); ++ input_report_abs(drv->input, ABS_X, ++ -(int16_t)(data[FRAME_POS_DATA_7] | (data[FRAME_POS_DATA_8] << 8))); ++ input_report_abs(drv->input, ABS_Y, ++ -(int16_t)(data[FRAME_POS_DATA_9] | (data[FRAME_POS_DATA_10] << 8))); ++ input_report_abs(drv->input, ABS_RX, ++ -(int16_t)(data[FRAME_POS_DATA_11] | (data[FRAME_POS_DATA_12] << 8))); ++ input_report_abs(drv->input, ABS_RY, ++ -(int16_t)(data[FRAME_POS_DATA_13] | (data[FRAME_POS_DATA_14] << 8))); ++ ++ input_sync(drv->input); ++ prev_states = keys; ++ ++ } else { ++ dev_warn(&drv->serdev->dev, "Invalid CMD_STATUS response length\n"); ++ } ++} ++ ++static void rsinput_process_data(struct rsinput_driver *drv, const uint8_t *data, size_t len) { ++ while (len >= MCU_PKT_SIZE_MIN) { ++ uint16_t payload_length = data[FRAME_POS_LEN_L] | (data[FRAME_POS_LEN_H] << 8); ++ size_t frame_length = MCU_PKT_SIZE_MIN + payload_length; ++ ++ if (len < frame_length) { ++ return; ++ } ++ ++ uint8_t received_checksum = data[frame_length - 1]; ++ uint8_t computed_checksum = compute_checksum(data, frame_length); ++ if (computed_checksum != received_checksum) { ++ data += frame_length; ++ len -= frame_length; ++ continue; ++ } ++ ++ switch (data[FRAME_POS_CMD]) { ++ case CMD_COMMOD: ++ handle_cmd_commod(drv, data, payload_length); ++ break; ++ case CMD_STATUS: ++ handle_cmd_status(drv, data, payload_length); ++ break; ++ default: ++ dev_warn(&drv->serdev->dev, "Unhandled command: 0x%02X\n", data[FRAME_POS_CMD]); ++ break; ++ } ++ ++ data += frame_length; ++ len -= frame_length; ++ } ++ ++ if (len > 0) { ++ dev_warn(&drv->serdev->dev, "Trailing bytes after processing: %zu\n", len); ++ } ++} ++ ++static size_t rsinput_rx(struct serdev_device *serdev, const u8 *data, size_t count) { ++ struct rsinput_driver *drv = serdev_device_get_drvdata(serdev); ++ uint8_t received_checksum, computed_checksum; ++ ++ if (!drv || !data || count == 0) { ++ dev_warn_ratelimited(&serdev->dev, "Invalid RX data\n"); ++ goto error; ++ } ++ ++ if (count > sizeof(drv->rx_buf)) { ++ dev_warn_ratelimited(&serdev->dev, "RX buffer overflow\n"); ++ goto error; ++ } ++ ++ memcpy(drv->rx_buf, data, count); ++ ++ if (count < MCU_PKT_SIZE_MIN) { ++ dev_warn_ratelimited(&serdev->dev, "Frame too short for checksum validation\n"); ++ goto error; ++ } ++ ++ received_checksum = drv->rx_buf[count - 1]; ++ ++ computed_checksum = compute_checksum(drv->rx_buf, count); ++ ++ if (computed_checksum != received_checksum) { ++ rsinput_init_commands(drv); ++ goto error; ++ } ++ ++ rsinput_process_data(drv, drv->rx_buf, count); ++ ++error: ++ return count; ++} ++ ++static const struct serdev_device_ops rsinput_rx_ops = { ++ .receive_buf = rsinput_rx, ++}; ++ ++static int rsinput_probe(struct serdev_device *serdev) { ++ struct rsinput_driver *drv; ++ u32 gamepad_bus = 0; ++ u32 gamepad_vid = 0; ++ u32 gamepad_pid = 0; ++ u32 gamepad_rev = 0; ++ int error; ++ ++ drv = devm_kzalloc(&serdev->dev, sizeof(*drv), GFP_KERNEL); ++ if (!drv) ++ return -ENOMEM; ++ ++ drv->boot_gpio = ++ devm_gpiod_get_optional(&serdev->dev, "boot", GPIOD_OUT_HIGH); ++ if (IS_ERR(drv->boot_gpio)) { ++ error = PTR_ERR(drv->boot_gpio); ++ dev_warn(&serdev->dev, "Unable to get boot gpio: %d\n", error); ++ } ++ ++ drv->enable_gpio = ++ devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_HIGH); ++ if (IS_ERR(drv->enable_gpio)) { ++ error = PTR_ERR(drv->enable_gpio); ++ dev_warn(&serdev->dev, "Unable to get enable gpio: %d\n", error); ++ } ++ ++ drv->reset_gpio = ++ devm_gpiod_get_optional(&serdev->dev, "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(drv->reset_gpio)) { ++ error = PTR_ERR(drv->reset_gpio); ++ dev_warn(&serdev->dev, "Unable to get reset gpio: %d\n", error); ++ } ++ ++ if (drv->boot_gpio) ++ gpiod_set_value_cansleep(drv->boot_gpio, 0); ++ ++ if (drv->reset_gpio) ++ gpiod_set_value_cansleep(drv->reset_gpio, 0); ++ ++ msleep(100); ++ ++ if (drv->enable_gpio) ++ gpiod_set_value_cansleep(drv->enable_gpio, 1); ++ ++ if (drv->reset_gpio) ++ gpiod_set_value_cansleep(drv->reset_gpio, 1); ++ ++ msleep(100); ++ ++ serdev_device_set_client_ops(serdev, &rsinput_rx_ops); ++ error = serdev_device_open(serdev); ++ if (error) ++ return dev_err_probe(&serdev->dev, error, "Unable to open UART device\n"); ++ ++ drv->serdev = serdev; ++ drv->sequence_number = 0; ++ ++ serdev_device_set_drvdata(serdev, drv); ++ ++ error = serdev_device_set_baudrate(serdev, 115200); ++ if (error < 0) ++ goto err_close; ++ ++ serdev_device_set_flow_control(serdev, false); ++ ++ drv->input = devm_input_allocate_device(&serdev->dev); ++ if (!drv->input) { ++ error = -ENOMEM; ++ goto err_close; ++ } ++ ++ drv->input->phys = "rsinput-gamepad/input0"; ++ ++ error = device_property_read_string(&serdev->dev, "gamepad-name", &drv->input->name); ++ if (error) { ++ drv->input->name = "RSInput Gamepad"; ++ } ++ ++ device_property_read_u32(&serdev->dev, "gamepad-bus", &gamepad_bus); ++ device_property_read_u32(&serdev->dev, "gamepad-vid", &gamepad_vid); ++ device_property_read_u32(&serdev->dev, "gamepad-pid", &gamepad_pid); ++ device_property_read_u32(&serdev->dev, "gamepad-rev", &gamepad_rev); ++ ++ drv->input->id.bustype = (u16)gamepad_bus; ++ drv->input->id.vendor = (u16)gamepad_vid; ++ drv->input->id.product = (u16)gamepad_pid; ++ drv->input->id.version = (u16)gamepad_rev; ++ ++ __set_bit(EV_KEY, drv->input->evbit); ++ for (int i = 0; i < ARRAY_SIZE(keymap); i++) ++ input_set_capability(drv->input, EV_KEY, keymap[i]); ++ ++ __set_bit(EV_ABS, drv->input->evbit); ++ for (int i = ABS_X; i <= ABS_RZ; i++) ++ input_set_abs_params(drv->input, i, -0x580, 0x580, ++ 0, 0); ++ ++ input_set_abs_params(drv->input, ABS_HAT2X, 0, 1830, 0, 30); ++ input_set_abs_params(drv->input, ABS_HAT2Y, 0, 1830, 0, 30); ++ ++ error = input_register_device(drv->input); ++ if (error) { ++ goto err_close; ++ } ++ ++ error = rsinput_init_commands(drv); ++ if (error < 0) { ++ goto err_close; ++ } ++ ++ return 0; ++ ++err_close: ++ serdev_device_close(serdev); ++ return error; ++} ++ ++static void rsinput_remove(struct serdev_device *serdev) { ++ struct rsinput_driver *drv = serdev_device_get_drvdata(serdev); ++ ++ serdev_device_close(serdev); ++ input_unregister_device(drv->input); ++ if (drv->enable_gpio) ++ gpiod_set_value_cansleep(drv->enable_gpio, 0); ++ ++ if (drv->reset_gpio) ++ gpiod_set_value_cansleep(drv->reset_gpio, 0); ++} ++ ++static const struct of_device_id rsinput_of_match[] = { ++ { .compatible = "gamepad,rsinput" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, rsinput_of_match); ++ ++static struct serdev_device_driver rsinput_driver = { ++ .probe = rsinput_probe, ++ .remove = rsinput_remove, ++ .driver = { ++ .name = "rsinput", ++ .of_match_table = rsinput_of_match, ++ }, ++}; ++ ++module_serdev_device_driver(rsinput_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("RSInput Gamepad Driver"); ++MODULE_AUTHOR("Teguh Sobirin "); +-- +2.43.0 + diff --git a/patch/kernel/archive/sm8550-6.19/0003-drm-panel-Add-panel-driver-for-Chipone-ICNA3512-base.patch b/patch/kernel/archive/sm8550-6.19/0003-drm-panel-Add-panel-driver-for-Chipone-ICNA3512-base.patch new file mode 100644 index 000000000000..16a04ef9301e --- /dev/null +++ b/patch/kernel/archive/sm8550-6.19/0003-drm-panel-Add-panel-driver-for-Chipone-ICNA3512-base.patch @@ -0,0 +1,565 @@ +From 273b6c9c953b71fe019792c5385ed720f7fa794b Mon Sep 17 00:00:00 2001 +From: Teguh Sobirin +Date: Thu, 13 Feb 2025 18:25:19 +0800 +Subject: [PATCH 03/15] drm/panel: Add panel driver for Chipone ICNA3512 based + panels + +Signed-off-by: Teguh Sobirin +--- + drivers/gpu/drm/panel/Kconfig | 11 + + drivers/gpu/drm/panel/Makefile | 1 + + .../gpu/drm/panel/panel-chipone-icna3512.c | 473 ++++++++++++++++++ + include/drm/drm_mipi_dsi.h | 22 + + 4 files changed, 507 insertions(+) + create mode 100644 drivers/gpu/drm/panel/panel-chipone-icna3512.c + +diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig +index 7a83804fedca..3c0d92cf48e2 100644 +--- a/drivers/gpu/drm/panel/Kconfig ++++ b/drivers/gpu/drm/panel/Kconfig +@@ -105,6 +105,17 @@ config DRM_PANEL_BOE_TV101WUM_LL2 + Say Y here if you want to support for BOE TV101WUM-LL2 + WUXGA PANEL DSI Video Mode panel + ++config DRM_PANEL_CHIPONE_ICNA3512 ++ tristate "Chipone ICNA3512 panel driver" ++ depends on OF ++ depends on DRM_MIPI_DSI ++ depends on BACKLIGHT_CLASS_DEVICE ++ select DRM_DISPLAY_HELPER ++ help ++ Say Y here if you want to enable support for the panels built ++ around the Chipone ICNA3512 display controller, such as some ++ Tianma panels used in AYN Odin2 Portal. ++ + config DRM_PANEL_EBBG_FT8719 + tristate "EBBG FT8719 panel driver" + depends on OF +diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile +index b9562a6fdcb3..7f3ca67bae75 100644 +--- a/drivers/gpu/drm/panel/Makefile ++++ b/drivers/gpu/drm/panel/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o + obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o + obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o + obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o ++obj-$(CONFIG_DRM_PANEL_CHIPONE_ICNA3512) += panel-chipone-icna3512.o + obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o + obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o + obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o +diff --git a/drivers/gpu/drm/panel/panel-chipone-icna3512.c b/drivers/gpu/drm/panel/panel-chipone-icna3512.c +new file mode 100644 +index 000000000000..cbda976df1db +--- /dev/null ++++ b/drivers/gpu/drm/panel/panel-chipone-icna3512.c +@@ -0,0 +1,473 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Chipone ICNA3512 Driver IC panels driver ++ * ++ * Copyright (c) 2025 Teguh Sobirin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include