* Re: [PATCH v5 3/4] clk: keystone: sci-clk: add restore_context() operation
From: Brian Masney @ 2026-04-08 22:30 UTC (permalink / raw)
To: Thomas Richard (TI)
Cc: Nishanth Menon, Tero Kristo, Santosh Shilimkar, Michael Turquette,
Stephen Boyd, Gregory CLEMENT, richard.genoud, Udit Kumar,
Prasanth Mantena, Abhash Kumar, Thomas Petazzoni,
linux-arm-kernel, linux-kernel, linux-clk, Dhruva Gole
In-Reply-To: <20260407-ti-sci-jacinto-s2r-restore-irq-v5-3-97b28f2d93f9@bootlin.com>
Hi Thomas,
On Tue, Apr 07, 2026 at 04:25:08PM +0200, Thomas Richard (TI) wrote:
> Implement the restore_context() operation to restore the clock rate and the
> clock parent state. The clock rate is saved in sci_clk struct during
> set_rate() operation. The parent index is saved in sci_clk struct during
> set_parent() operation. During clock registration, the core retrieves each
> clock’s parent using get_parent() operation to ensure the internal clock
> tree reflects the actual hardware state, including any configurations made
> by the bootloader. So we also save the parent index in get_parent().
>
> Reviewed-by: Dhruva Gole <d-gole@ti.com>
> Signed-off-by: Thomas Richard (TI) <thomas.richard@bootlin.com>
> ---
> drivers/clk/keystone/sci-clk.c | 42 ++++++++++++++++++++++++++++++++++--------
> 1 file changed, 34 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c
> index 9d5071223f4c..428050a05de3 100644
> --- a/drivers/clk/keystone/sci-clk.c
> +++ b/drivers/clk/keystone/sci-clk.c
> @@ -47,6 +47,8 @@ struct sci_clk_provider {
> * @node: Link for handling clocks probed via DT
> * @cached_req: Cached requested freq for determine rate calls
> * @cached_res: Cached result freq for determine rate calls
> + * @parent_id: Parent index for this clock
> + * @rate: Clock rate
> */
> struct sci_clk {
> struct clk_hw hw;
> @@ -58,6 +60,8 @@ struct sci_clk {
> struct list_head node;
> unsigned long cached_req;
> unsigned long cached_res;
> + u8 parent_id;
> + unsigned long rate;
> };
>
> #define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw)
> @@ -210,10 +214,16 @@ static int sci_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> unsigned long parent_rate)
> {
> struct sci_clk *clk = to_sci_clk(hw);
> + int ret;
> +
> + ret = clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id,
> + clk->clk_id, rate / 10 * 9, rate,
> + rate / 10 * 11);
>
> - return clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id,
> - clk->clk_id, rate / 10 * 9, rate,
> - rate / 10 * 11);
> + if (!ret)
> + clk->rate = rate;
> +
> + return ret;
> }
Should the computed rate from sci_clk_recalc_rate() be saved as well?
>
> /**
> @@ -237,9 +247,9 @@ static u8 sci_clk_get_parent(struct clk_hw *hw)
> return 0;
> }
>
> - parent_id = parent_id - clk->clk_id - 1;
> + clk->parent_id = (u8)(parent_id - clk->clk_id - 1);
>
> - return (u8)parent_id;
> + return clk->parent_id;
> }
>
> /**
> @@ -252,12 +262,27 @@ static u8 sci_clk_get_parent(struct clk_hw *hw)
> static int sci_clk_set_parent(struct clk_hw *hw, u8 index)
> {
> struct sci_clk *clk = to_sci_clk(hw);
> + int ret;
>
> clk->cached_req = 0;
>
> - return clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id,
> - clk->clk_id,
> - index + 1 + clk->clk_id);
> + ret = clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id,
> + clk->clk_id,
> + index + 1 + clk->clk_id);
> + if (!ret)
> + clk->parent_id = index;
> +
> + return ret;
> +}
> +
> +static void sci_clk_restore_context(struct clk_hw *hw)
> +{
> + struct sci_clk *clk = to_sci_clk(hw);
> +
> + sci_clk_set_parent(hw, clk->parent_id);
Are all of these clocks muxes?
Brian
> +
> + if (clk->rate)
> + sci_clk_set_rate(hw, clk->rate, 0);
> }
>
> static const struct clk_ops sci_clk_ops = {
> @@ -269,6 +294,7 @@ static const struct clk_ops sci_clk_ops = {
> .set_rate = sci_clk_set_rate,
> .get_parent = sci_clk_get_parent,
> .set_parent = sci_clk_set_parent,
> + .restore_context = sci_clk_restore_context,
> };
>
> /**
>
> --
> 2.53.0
>
^ permalink raw reply
* [PATCH v4 10/11] Input: stmfts - support FTS5
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: Petr Hodina <petr.hodina@protonmail.com>
FTS support SLPI and AP mode, introduce mode-switch GPIO to switch between
those two. Currently we can handle only full power AP mode, so we just
keep the AP on.
Useful for devices like Pixel 3 (blueline) and many others.
Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 494 ++++++++++++++++++++++++++++++++++---
1 file changed, 466 insertions(+), 28 deletions(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 1e6d9a287cd0c..e613299e37557 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
-// STMicroelectronics FTS Touchscreen device driver
-//
-// Copyright (c) 2017 Samsung Electronics Co., Ltd.
-// Copyright (c) 2017 Andi Shyti <andi@etezian.org>
+/* STMicroelectronics FTS Touchscreen device driver
+ *
+ * Copyright 2017 Samsung Electronics Co., Ltd.
+ * Copyright 2017 Andi Shyti <andi@etezian.org>
+ * Copyright David Heidelberg <david@ixit.cz>
+ * Copyright Petr Hodina <petr.hodina@protonmail.com>
+ */
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -12,6 +15,7 @@
#include <linux/irq.h>
#include <linux/leds.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -34,6 +38,7 @@
#define STMFTS_FULL_FORCE_CALIBRATION 0xa2
#define STMFTS_MS_CX_TUNING 0xa3
#define STMFTS_SS_CX_TUNING 0xa4
+#define STMFTS5_SET_SCAN_MODE 0xa0
/* events */
#define STMFTS_EV_NO_EVENT 0x00
@@ -51,12 +56,32 @@
#define STMFTS_EV_STATUS 0x16
#define STMFTS_EV_DEBUG 0xdb
+/* events FTS5 */
+#define STMFTS5_EV_CONTROLLER_READY 0x03
+/* FTM5 event IDs (full byte, not masked) */
+#define STMFTS5_EV_MULTI_TOUCH_ENTER 0x13
+#define STMFTS5_EV_MULTI_TOUCH_MOTION 0x23
+#define STMFTS5_EV_MULTI_TOUCH_LEAVE 0x33
+#define STMFTS5_EV_STATUS_UPDATE 0x43
+#define STMFTS5_EV_USER_REPORT 0x53
+#define STMFTS5_EV_DEBUG 0xe3
+#define STMFTS5_EV_ERROR 0xf3
+
/* multi touch related event masks */
#define STMFTS_MASK_EVENT_ID 0x0f
#define STMFTS_MASK_TOUCH_ID 0xf0
#define STMFTS_MASK_LEFT_EVENT 0x0f
#define STMFTS_MASK_X_MSB 0x0f
#define STMFTS_MASK_Y_LSB 0xf0
+#define STMFTS5_MASK_TOUCH_TYPE 0x0f
+
+/* touch type classifications */
+#define STMFTS_TOUCH_TYPE_INVALID 0x00
+#define STMFTS_TOUCH_TYPE_FINGER 0x01
+#define STMFTS_TOUCH_TYPE_GLOVE 0x02
+#define STMFTS_TOUCH_TYPE_STYLUS 0x03
+#define STMFTS_TOUCH_TYPE_PALM 0x04
+#define STMFTS_TOUCH_TYPE_HOVER 0x05
/* key related event masks */
#define STMFTS_MASK_KEY_NO_TOUCH 0x00
@@ -75,9 +100,12 @@ static const struct regulator_bulk_data stmfts_supplies[] = {
};
struct stmfts_data {
+ const struct stmfts_chip_ops *ops;
+
struct i2c_client *client;
struct input_dev *input;
struct gpio_desc *reset_gpio;
+ struct gpio_desc *mode_switch_gpio;
struct led_classdev led_cdev;
struct mutex mutex;
@@ -101,12 +129,27 @@ struct stmfts_data {
struct completion cmd_done;
+ unsigned long touch_id;
+ unsigned long stylus_id;
+
bool use_key;
bool led_status;
bool hover_enabled;
+ bool stylus_enabled;
bool running;
};
+struct stmfts_chip_ops {
+ int (*configure)(struct stmfts_data *sdata);
+ void (*power_off)(struct stmfts_data *sdata);
+ int (*setup_input)(struct stmfts_data *sdata);
+ int (*input_open)(struct input_dev *dev);
+ void (*input_close)(struct input_dev *dev);
+ void (*parse_events)(struct stmfts_data *sdata);
+ int (*set_hover)(struct stmfts_data *sdata, bool enable);
+ int (*runtime_resume)(struct stmfts_data *sdata);
+};
+
static int stmfts_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
@@ -169,6 +212,7 @@ static int stmfts_read_events(struct stmfts_data *sdata)
return ret == ARRAY_SIZE(msgs) ? 0 : -EIO;
}
+/* FTS4 event handling functions */
static void stmfts_report_contact_event(struct stmfts_data *sdata,
const u8 event[])
{
@@ -204,6 +248,157 @@ static void stmfts_report_contact_release(struct stmfts_data *sdata,
input_sync(sdata->input);
}
+/* FTS5 event handling functions */
+static void stmfts5_report_contact_event(struct stmfts_data *sdata,
+ const u8 event[])
+{
+ u8 area;
+ u8 maj;
+ u8 min;
+ /* FTM5 event format:
+ * event[0] = event ID (0x13/0x23)
+ * event[1] = touch type (low 4 bits) | touch ID (high 4 bits)
+ * event[2] = X LSB
+ * event[3] = X MSB (low 4 bits) | Y MSB (high 4 bits)
+ * event[4] = Y LSB
+ * event[5] = pressure
+ * event[6] = major (low 4 bits) | minor (high 4 bits)
+ * event[7] = minor (high 2 bits)
+ */
+ u8 touch_id = (event[1] & STMFTS_MASK_TOUCH_ID) >> 4;
+ u8 touch_type = event[1] & STMFTS5_MASK_TOUCH_TYPE;
+ int x, y, distance;
+ unsigned int tool = MT_TOOL_FINGER;
+ bool touch_condition = true;
+
+ /* Parse coordinates with better precision */
+ x = (((int)event[3] & STMFTS_MASK_X_MSB) << 8) | event[2];
+ y = ((int)event[4] << 4) | ((event[3] & STMFTS_MASK_Y_LSB) >> 4);
+
+ /* Parse pressure - ensure non-zero for active touch */
+ area = event[5];
+ if (area <= 0 && touch_type != STMFTS_TOUCH_TYPE_HOVER) {
+ /* Should not happen for contact events. Set minimum pressure
+ * to prevent touch from being dropped
+ */
+ dev_warn_once(&sdata->client->dev,
+ "zero pressure on contact event, slot %d\n", touch_id);
+ area = 1;
+ }
+
+ /* Parse touch area with improved bit extraction */
+ maj = (((event[0] & 0x0C) << 2) | ((event[6] & 0xF0) >> 4));
+ min = (((event[7] & 0xC0) >> 2) | (event[6] & 0x0F));
+
+ /* Distance is 0 for touching, max for hovering */
+ distance = 0;
+
+ /* Classify touch type and set appropriate tool and parameters */
+ switch (touch_type) {
+ case STMFTS_TOUCH_TYPE_STYLUS:
+ if (sdata->stylus_enabled) {
+ tool = MT_TOOL_PEN;
+ __set_bit(touch_id, &sdata->stylus_id);
+ __clear_bit(touch_id, &sdata->touch_id);
+ break;
+ }
+ fallthrough; /* Report as finger if stylus not enabled */
+
+ case STMFTS_TOUCH_TYPE_FINGER:
+ case STMFTS_TOUCH_TYPE_GLOVE:
+ tool = MT_TOOL_FINGER;
+ __set_bit(touch_id, &sdata->touch_id);
+ __clear_bit(touch_id, &sdata->stylus_id);
+ break;
+
+ case STMFTS_TOUCH_TYPE_PALM:
+ /* Palm touch - report but can be filtered by userspace */
+ tool = MT_TOOL_PALM;
+ __set_bit(touch_id, &sdata->touch_id);
+ __clear_bit(touch_id, &sdata->stylus_id);
+ break;
+
+ case STMFTS_TOUCH_TYPE_HOVER:
+ tool = MT_TOOL_FINGER;
+ touch_condition = false;
+ area = 0;
+ distance = 255;
+ __set_bit(touch_id, &sdata->touch_id);
+ __clear_bit(touch_id, &sdata->stylus_id);
+ break;
+
+ case STMFTS_TOUCH_TYPE_INVALID:
+ default:
+ dev_warn(&sdata->client->dev,
+ "invalid touch type %d for slot %d\n",
+ touch_type, touch_id);
+ return;
+ }
+
+ /* Boundary check - some devices report max value, adjust */
+ if (x >= sdata->prop.max_x)
+ x = sdata->prop.max_x - 1;
+ if (y >= sdata->prop.max_y)
+ y = sdata->prop.max_y - 1;
+
+ input_mt_slot(sdata->input, touch_id);
+ input_report_key(sdata->input, BTN_TOUCH, touch_condition);
+ input_mt_report_slot_state(sdata->input, tool, true);
+
+ input_report_abs(sdata->input, ABS_MT_POSITION_X, x);
+ input_report_abs(sdata->input, ABS_MT_POSITION_Y, y);
+ input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj);
+ input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min);
+ input_report_abs(sdata->input, ABS_MT_PRESSURE, area);
+ input_report_abs(sdata->input, ABS_MT_DISTANCE, distance);
+
+ input_sync(sdata->input);
+}
+
+static void stmfts5_report_contact_release(struct stmfts_data *sdata,
+ const u8 event[])
+{
+ /* FTM5 format: touch ID is in high 4 bits of event[1] */
+ u8 touch_id = (event[1] & STMFTS_MASK_TOUCH_ID) >> 4;
+ u8 touch_type = event[1] & STMFTS5_MASK_TOUCH_TYPE;
+ unsigned int tool = MT_TOOL_FINGER;
+
+ /* Determine tool type based on touch classification */
+ switch (touch_type) {
+ case STMFTS_TOUCH_TYPE_STYLUS:
+ if (sdata->stylus_enabled) {
+ tool = MT_TOOL_PEN;
+ __clear_bit(touch_id, &sdata->stylus_id);
+ } else {
+ __clear_bit(touch_id, &sdata->touch_id);
+ }
+ break;
+
+ case STMFTS_TOUCH_TYPE_PALM:
+ tool = MT_TOOL_PALM;
+ __clear_bit(touch_id, &sdata->touch_id);
+ break;
+
+ case STMFTS_TOUCH_TYPE_FINGER:
+ case STMFTS_TOUCH_TYPE_GLOVE:
+ case STMFTS_TOUCH_TYPE_HOVER:
+ default:
+ tool = MT_TOOL_FINGER;
+ __clear_bit(touch_id, &sdata->touch_id);
+ break;
+ }
+
+ input_mt_slot(sdata->input, touch_id);
+ input_report_abs(sdata->input, ABS_MT_PRESSURE, 0);
+ input_mt_report_slot_state(sdata->input, tool, false);
+
+ /* Report BTN_TOUCH only if no touches remain */
+ if (!sdata->touch_id && !sdata->stylus_id)
+ input_report_key(sdata->input, BTN_TOUCH, 0);
+
+ input_sync(sdata->input);
+}
+
static void stmfts_report_hover_event(struct stmfts_data *sdata,
const u8 event[])
{
@@ -251,7 +446,6 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE];
switch (event[0]) {
-
case STMFTS_EV_CONTROLLER_READY:
case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
case STMFTS_EV_STATUS:
@@ -264,7 +458,6 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
}
switch (event[0] & STMFTS_MASK_EVENT_ID) {
-
case STMFTS_EV_MULTI_TOUCH_ENTER:
case STMFTS_EV_MULTI_TOUCH_MOTION:
stmfts_report_contact_event(sdata, event);
@@ -298,6 +491,45 @@ static void stmfts_parse_events(struct stmfts_data *sdata)
}
}
+static void stmfts5_parse_events(struct stmfts_data *sdata)
+{
+ for (int i = 0; i < STMFTS_STACK_DEPTH; i++) {
+ u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE];
+
+ switch (event[0]) {
+ case STMFTS5_EV_CONTROLLER_READY:
+ complete(&sdata->cmd_done);
+ fallthrough;
+
+ case STMFTS_EV_NO_EVENT:
+ case STMFTS5_EV_STATUS_UPDATE:
+ case STMFTS5_EV_USER_REPORT:
+ case STMFTS5_EV_DEBUG:
+ return;
+
+ case STMFTS5_EV_MULTI_TOUCH_ENTER:
+ case STMFTS5_EV_MULTI_TOUCH_MOTION:
+ stmfts5_report_contact_event(sdata, event);
+ break;
+
+ case STMFTS5_EV_MULTI_TOUCH_LEAVE:
+ stmfts5_report_contact_release(sdata, event);
+ break;
+
+ case STMFTS5_EV_ERROR:
+ dev_warn(&sdata->client->dev,
+ "error code: 0x%x%x%x%x%x%x",
+ event[6], event[5], event[4],
+ event[3], event[2], event[1]);
+ break;
+
+ default:
+ dev_err(&sdata->client->dev,
+ "unknown FTS5 event %#02x\n", event[0]);
+ }
+ }
+}
+
static irqreturn_t stmfts_irq_handler(int irq, void *dev)
{
struct stmfts_data *sdata = dev;
@@ -310,7 +542,7 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev)
dev_err(&sdata->client->dev,
"failed to read events: %d\n", err);
else
- stmfts_parse_events(sdata);
+ sdata->ops->parse_events(sdata);
return IRQ_HANDLED;
}
@@ -332,6 +564,25 @@ static int stmfts_command(struct stmfts_data *sdata, const u8 cmd)
return 0;
}
+static int stmfts5_set_scan_mode(struct stmfts_data *sdata, const u8 val)
+{
+ int err;
+
+ u8 scan_mode_cmd[3] = { STMFTS5_SET_SCAN_MODE, 0x00, val };
+ struct i2c_msg msg = {
+ .addr = sdata->client->addr,
+ .len = sizeof(scan_mode_cmd),
+ .buf = scan_mode_cmd,
+ };
+
+ err = i2c_transfer(sdata->client->adapter, &msg, 1);
+ if (err != 1)
+ return err < 0 ? err : -EIO;
+
+ return 0;
+
+}
+
static int stmfts_input_open(struct input_dev *dev)
{
struct stmfts_data *sdata = input_get_drvdata(dev);
@@ -371,6 +622,28 @@ static int stmfts_input_open(struct input_dev *dev)
return 0;
}
+static int stmfts5_input_open(struct input_dev *dev)
+{
+ struct stmfts_data *sdata = input_get_drvdata(dev);
+ int err;
+
+ err = pm_runtime_resume_and_get(&sdata->client->dev);
+ if (err)
+ return err;
+
+ mutex_lock(&sdata->mutex);
+ sdata->running = true;
+ mutex_unlock(&sdata->mutex);
+
+ err = stmfts5_set_scan_mode(sdata, 0xff);
+ if (err) {
+ pm_runtime_put_sync(&sdata->client->dev);
+ return err;
+ }
+
+ return 0;
+}
+
static void stmfts_input_close(struct input_dev *dev)
{
struct stmfts_data *sdata = input_get_drvdata(dev);
@@ -404,6 +677,23 @@ static void stmfts_input_close(struct input_dev *dev)
pm_runtime_put_sync(&sdata->client->dev);
}
+static void stmfts5_input_close(struct input_dev *dev)
+{
+ struct stmfts_data *sdata = input_get_drvdata(dev);
+ int err;
+
+ err = stmfts5_set_scan_mode(sdata, 0x00);
+ if (err)
+ dev_warn(&sdata->client->dev,
+ "failed to disable touchscreen: %d\n", err);
+
+ mutex_lock(&sdata->mutex);
+ sdata->running = false;
+ mutex_unlock(&sdata->mutex);
+
+ pm_runtime_put_sync(&sdata->client->dev);
+}
+
static ssize_t stmfts_sysfs_chip_id(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -484,10 +774,8 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
guard(mutex)(&sdata->mutex);
if (hover != sdata->hover_enabled) {
- if (sdata->running) {
- err = i2c_smbus_write_byte(sdata->client,
- value ? STMFTS_SS_HOVER_SENSE_ON :
- STMFTS_SS_HOVER_SENSE_OFF);
+ if (sdata->running && sdata->ops->set_hover) {
+ err = sdata->ops->set_hover(sdata, hover);
if (err)
return err;
}
@@ -612,7 +900,7 @@ static int stmfts_power_on(struct stmfts_data *sdata)
if (sdata->reset_gpio)
stmfts_reset(sdata);
- err = stmfts_configure(sdata);
+ err = sdata->ops->configure(sdata);
if (err)
regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
sdata->supplies);
@@ -620,6 +908,29 @@ static int stmfts_power_on(struct stmfts_data *sdata)
return err;
}
+static int stmfts5_configure(struct stmfts_data *sdata)
+{
+ u8 event[STMFTS_EVENT_SIZE];
+ int ret;
+
+ /* Verify I2C communication */
+ ret = i2c_smbus_read_i2c_block_data(sdata->client,
+ STMFTS_READ_ALL_EVENT,
+ sizeof(event), event);
+ if (ret < 0)
+ return ret;
+
+ enable_irq(sdata->client->irq);
+
+ return 0;
+}
+
+static void stmfts5_chip_power_off(struct stmfts_data *sdata)
+{
+ i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN);
+ msleep(20);
+}
+
static void stmfts_power_off(void *data)
{
struct stmfts_data *sdata = data;
@@ -629,10 +940,73 @@ static void stmfts_power_off(void *data)
if (sdata->reset_gpio)
gpiod_set_value_cansleep(sdata->reset_gpio, 1);
+ if (sdata->ops->power_off)
+ sdata->ops->power_off(sdata);
+
regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
sdata->supplies);
}
+static int stmfts_setup_input(struct stmfts_data *sdata)
+{
+ struct device *dev = &sdata->client->dev;
+
+ input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0);
+ input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
+
+ sdata->use_key = device_property_read_bool(dev, "touch-key-connected");
+ if (sdata->use_key) {
+ input_set_capability(sdata->input, EV_KEY, KEY_MENU);
+ input_set_capability(sdata->input, EV_KEY, KEY_BACK);
+ }
+
+ return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS,
+ INPUT_MT_DIRECT);
+}
+
+static int stmfts5_setup_input(struct stmfts_data *sdata)
+{
+ struct device *dev = &sdata->client->dev;
+
+ sdata->mode_switch_gpio = devm_gpiod_get_optional(dev, "mode-switch",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sdata->mode_switch_gpio))
+ return dev_err_probe(dev, PTR_ERR(sdata->mode_switch_gpio),
+ "Failed to get GPIO 'switch'\n");
+
+ /* Mark as direct input device for calibration support */
+ __set_bit(INPUT_PROP_DIRECT, sdata->input->propbit);
+
+ /* Set up basic touch capabilities */
+ input_set_capability(sdata->input, EV_KEY, BTN_TOUCH);
+
+ /* Set resolution for accurate calibration */
+ if (!input_abs_get_res(sdata->input, ABS_MT_POSITION_X)) {
+ input_abs_set_res(sdata->input, ABS_MT_POSITION_X, 10);
+ input_abs_set_res(sdata->input, ABS_MT_POSITION_Y, 10);
+ }
+
+ input_set_abs_params(sdata->input, ABS_MT_DISTANCE, 0, 255, 0, 0);
+
+ /* Enable stylus support if requested */
+ sdata->stylus_enabled = device_property_read_bool(dev, "stylus-enabled");
+
+ /* Initialize touch tracking bitmaps */
+ sdata->touch_id = 0;
+ sdata->stylus_id = 0;
+
+ /* Initialize MT slots with support for pen tool type */
+ return input_mt_init_slots(sdata->input, STMFTS_MAX_FINGERS,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+}
+
+static int stmfts_set_hover(struct stmfts_data *sdata, bool enable)
+{
+ return i2c_smbus_write_byte(sdata->client,
+ enable ? STMFTS_SS_HOVER_SENSE_ON :
+ STMFTS_SS_HOVER_SENSE_OFF);
+}
+
static int stmfts_enable_led(struct stmfts_data *sdata)
{
int err;
@@ -678,6 +1052,8 @@ static int stmfts_probe(struct i2c_client *client)
mutex_init(&sdata->mutex);
init_completion(&sdata->cmd_done);
+ sdata->ops = of_device_get_match_data(dev);
+
err = devm_regulator_bulk_get_const(dev,
ARRAY_SIZE(stmfts_supplies),
stmfts_supplies,
@@ -697,8 +1073,8 @@ static int stmfts_probe(struct i2c_client *client)
sdata->input->name = STMFTS_DEV_NAME;
sdata->input->id.bustype = BUS_I2C;
- sdata->input->open = stmfts_input_open;
- sdata->input->close = stmfts_input_close;
+ sdata->input->open = sdata->ops->input_open;
+ sdata->input->close = sdata->ops->input_close;
input_set_capability(sdata->input, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(sdata->input, EV_ABS, ABS_MT_POSITION_Y);
@@ -706,19 +1082,9 @@ static int stmfts_probe(struct i2c_client *client)
input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
- input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0);
input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
- input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
-
- sdata->use_key = device_property_read_bool(dev,
- "touch-key-connected");
- if (sdata->use_key) {
- input_set_capability(sdata->input, EV_KEY, KEY_MENU);
- input_set_capability(sdata->input, EV_KEY, KEY_BACK);
- }
- err = input_mt_init_slots(sdata->input,
- STMFTS_MAX_FINGERS, INPUT_MT_DIRECT);
+ err = sdata->ops->setup_input(sdata);
if (err)
return err;
@@ -789,13 +1155,62 @@ static int stmfts_runtime_suspend(struct device *dev)
return ret;
}
-static int stmfts_runtime_resume(struct device *dev)
+static int stmfts_chip_runtime_resume(struct stmfts_data *sdata)
+{
+ return i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT);
+}
+
+static int stmfts5_chip_runtime_resume(struct stmfts_data *sdata)
{
- struct stmfts_data *sdata = dev_get_drvdata(dev);
struct i2c_client *client = sdata->client;
+ struct device *dev = &client->dev;
+ u8 int_enable_cmd[4] = {0xB6, 0x00, 0x2C, 0x01};
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .len = sizeof(int_enable_cmd),
+ .buf = int_enable_cmd,
+ };
int ret;
ret = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT);
+ if (ret)
+ return ret;
+
+ msleep(20);
+
+ /* Perform capacitance tuning after wakeup */
+ ret = i2c_smbus_write_byte(client, STMFTS_MS_CX_TUNING);
+ if (ret)
+ dev_warn(dev, "MS_CX_TUNING failed: %d\n", ret);
+ msleep(20);
+
+ ret = i2c_smbus_write_byte(client, STMFTS_SS_CX_TUNING);
+ if (ret)
+ dev_warn(dev, "SS_CX_TUNING failed: %d\n", ret);
+ msleep(20);
+
+ /* Force calibration */
+ ret = i2c_smbus_write_byte(client, STMFTS_FULL_FORCE_CALIBRATION);
+ if (ret)
+ dev_warn(dev, "FORCE_CALIBRATION failed: %d\n", ret);
+ msleep(50);
+
+ /* Enable controller interrupts */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1)
+ return ret < 0 ? ret : -EIO;
+
+ msleep(20);
+
+ return 0;
+}
+
+static int stmfts_runtime_resume(struct device *dev)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sdata->ops->runtime_resume(sdata);
if (ret)
dev_err(dev, "failed to resume device: %d\n", ret);
@@ -824,8 +1239,29 @@ static const struct dev_pm_ops stmfts_pm_ops = {
};
#ifdef CONFIG_OF
+static const struct stmfts_chip_ops stmfts4_ops = {
+ .configure = stmfts_configure,
+ .setup_input = stmfts_setup_input,
+ .input_open = stmfts_input_open,
+ .input_close = stmfts_input_close,
+ .parse_events = stmfts_parse_events,
+ .set_hover = stmfts_set_hover,
+ .runtime_resume = stmfts_chip_runtime_resume,
+};
+
+static const struct stmfts_chip_ops stmfts5_ops = {
+ .configure = stmfts5_configure,
+ .power_off = stmfts5_chip_power_off,
+ .setup_input = stmfts5_setup_input,
+ .input_open = stmfts5_input_open,
+ .input_close = stmfts5_input_close,
+ .parse_events = stmfts5_parse_events,
+ .runtime_resume = stmfts5_chip_runtime_resume,
+};
+
static const struct of_device_id stmfts_of_match[] = {
- { .compatible = "st,stmfts", },
+ { .compatible = "st,stmfts", .data = &stmfts4_ops },
+ { .compatible = "st,stmfts5", .data = &stmfts5_ops },
{ },
};
MODULE_DEVICE_TABLE(of, stmfts_of_match);
@@ -853,5 +1289,7 @@ static struct i2c_driver stmfts_driver = {
module_i2c_driver(stmfts_driver);
MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
+MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
+MODULE_AUTHOR("Petr Hodina <petr.hodina@protonmail.com>");
MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen");
MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH v4 11/11] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg, Konrad Dybcio
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: Petr Hodina <petr.hodina@protonmail.com>
Basic touchscreen connected to second i2c bus.
Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts | 19 ++++++++++++++++++-
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 2 +-
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
index fa89be500fb85..8fb988130b551 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
@@ -26,7 +26,24 @@ &i2c2 {
status = "okay";
- /* ST,FTS @ 49 */
+ touchscreen@49 {
+ compatible = "st,stmfts5";
+ reg = <0x49>;
+
+ pinctrl-0 = <&touchscreen_irq_n>, <&touchscreen_reset>;
+ pinctrl-names = "default";
+
+ interrupts-extended = <&tlmm 125 IRQ_TYPE_LEVEL_LOW>;
+
+ mode-switch-gpios = <&tlmm 136 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&vreg_l14a_1p8>;
+ vdd-supply = <&vreg_l19a_3p3>;
+
+ touchscreen-size-x = <1080>;
+ touchscreen-size-y = <2160>;
+ };
};
&mdss_dsi0 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
index 6930066857768..4653c63ec26d2 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
@@ -466,7 +466,7 @@ touchscreen_reset: ts-reset-state {
bias-pull-up;
};
- touchscreen_pins: ts-pins-gpio-state {
+ touchscreen_irq_n: ts-irq-n-gpio-state {
pins = "gpio125";
function = "gpio";
drive-strength = <2>;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 08/11] Input: stmfts - add optional reset GPIO support
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: Petr Hodina <petr.hodina@protonmail.com>
Add support for an optional "reset-gpios" property. If present, the
driver drives the reset line high at probe time and releases it during
power-on, after the regulators have been enabled.
Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index b61a19e954296..1e6d9a287cd0c 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -77,6 +77,7 @@ static const struct regulator_bulk_data stmfts_supplies[] = {
struct stmfts_data {
struct i2c_client *client;
struct input_dev *input;
+ struct gpio_desc *reset_gpio;
struct led_classdev led_cdev;
struct mutex mutex;
@@ -539,6 +540,15 @@ static int stmfts_read_system_info(struct stmfts_data *sdata)
return 0;
}
+static void stmfts_reset(struct stmfts_data *sdata)
+{
+ gpiod_set_value_cansleep(sdata->reset_gpio, 1);
+ msleep(20);
+
+ gpiod_set_value_cansleep(sdata->reset_gpio, 0);
+ msleep(50);
+}
+
static int stmfts_configure(struct stmfts_data *sdata)
{
int err;
@@ -599,6 +609,9 @@ static int stmfts_power_on(struct stmfts_data *sdata)
*/
msleep(20);
+ if (sdata->reset_gpio)
+ stmfts_reset(sdata);
+
err = stmfts_configure(sdata);
if (err)
regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
@@ -612,6 +625,10 @@ static void stmfts_power_off(void *data)
struct stmfts_data *sdata = data;
disable_irq(sdata->client->irq);
+
+ if (sdata->reset_gpio)
+ gpiod_set_value_cansleep(sdata->reset_gpio, 1);
+
regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
sdata->supplies);
}
@@ -668,6 +685,12 @@ static int stmfts_probe(struct i2c_client *client)
if (err)
return err;
+ sdata->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sdata->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(sdata->reset_gpio),
+ "Failed to get GPIO 'reset'\n");
+
sdata->input = devm_input_allocate_device(dev);
if (!sdata->input)
return -ENOMEM;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 09/11] dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Introduce more recent STM FTS5 touchscreen support.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
.../devicetree/bindings/input/touchscreen/st,stmfts.yaml | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
index 64c4f24ea3dd0..441fc92b9a4ed 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
@@ -16,10 +16,19 @@ description:
allOf:
- $ref: touchscreen.yaml#
+ - if:
+ properties:
+ compatible:
+ const: st,stmfts5
+ then:
+ required:
+ - mode-switch-gpios
properties:
compatible:
- const: st,stmfts
+ enum:
+ - st,stmfts
+ - st,stmfts5
reg:
maxItems: 1
@@ -40,6 +49,10 @@ properties:
vdd-supply:
description: Power supply
+ mode-switch-gpios:
+ description: Switch between touchscreen SLPI and AP mode.
+ maxItems: 1
+
reset-gpios:
description: Reset GPIO (active-low)
maxItems: 1
--
2.53.0
^ permalink raw reply related
* [PATCH v4 02/11] Input: stmfts - Use dev struct directly
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Makes the code better readable and noticably shorter.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index def6bd0c8e059..7b1e975a85668 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -619,6 +619,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata)
static int stmfts_probe(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
int err;
struct stmfts_data *sdata;
@@ -627,7 +628,7 @@ static int stmfts_probe(struct i2c_client *client)
I2C_FUNC_SMBUS_I2C_BLOCK))
return -ENODEV;
- sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL);
+ sdata = devm_kzalloc(dev, sizeof(*sdata), GFP_KERNEL);
if (!sdata)
return -ENOMEM;
@@ -639,13 +640,13 @@ static int stmfts_probe(struct i2c_client *client)
sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd";
sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd";
- err = devm_regulator_bulk_get(&client->dev,
+ err = devm_regulator_bulk_get(dev,
ARRAY_SIZE(sdata->regulators),
sdata->regulators);
if (err)
return err;
- sdata->input = devm_input_allocate_device(&client->dev);
+ sdata->input = devm_input_allocate_device(dev);
if (!sdata->input)
return -ENOMEM;
@@ -664,7 +665,7 @@ static int stmfts_probe(struct i2c_client *client)
input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
- sdata->use_key = device_property_read_bool(&client->dev,
+ sdata->use_key = device_property_read_bool(dev,
"touch-key-connected");
if (sdata->use_key) {
input_set_capability(sdata->input, EV_KEY, KEY_MENU);
@@ -685,20 +686,20 @@ static int stmfts_probe(struct i2c_client *client)
* interrupts. To be on the safe side it's better to not enable
* the interrupts during their request.
*/
- err = devm_request_threaded_irq(&client->dev, client->irq,
+ err = devm_request_threaded_irq(dev, client->irq,
NULL, stmfts_irq_handler,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"stmfts_irq", sdata);
if (err)
return err;
- dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n");
+ dev_dbg(dev, "initializing ST-Microelectronics FTS...\n");
err = stmfts_power_on(sdata);
if (err)
return err;
- err = devm_add_action_or_reset(&client->dev, stmfts_power_off, sdata);
+ err = devm_add_action_or_reset(dev, stmfts_power_off, sdata);
if (err)
return err;
@@ -715,13 +716,13 @@ static int stmfts_probe(struct i2c_client *client)
* without LEDs. The ledvdd regulator pointer will be
* used as a flag.
*/
- dev_warn(&client->dev, "unable to use touchkey leds\n");
+ dev_warn(dev, "unable to use touchkey leds\n");
sdata->ledvdd = NULL;
}
}
- pm_runtime_enable(&client->dev);
- device_enable_async_suspend(&client->dev);
+ pm_runtime_enable(dev);
+ device_enable_async_suspend(dev);
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v4 07/11] dt-bindings: input: touchscreen: st,stmfts: Introduce reset GPIO
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
FTS has associated reset GPIO, document it.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
index 12256ae7df90d..64c4f24ea3dd0 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
@@ -40,6 +40,10 @@ properties:
vdd-supply:
description: Power supply
+ reset-gpios:
+ description: Reset GPIO (active-low)
+ maxItems: 1
+
required:
- compatible
- reg
--
2.53.0
^ permalink raw reply related
* [PATCH v4 06/11] Input: stmfts - use client to make future code cleaner
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: Petr Hodina <petr.hodina@protonmail.com>
Make code cleaner, compiler will optimize it away anyway.
Preparation for FTM5 support, where more steps are needed.
Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 290511dd69437..b61a19e954296 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -769,9 +769,10 @@ static int stmfts_runtime_suspend(struct device *dev)
static int stmfts_runtime_resume(struct device *dev)
{
struct stmfts_data *sdata = dev_get_drvdata(dev);
+ struct i2c_client *client = sdata->client;
int ret;
- ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT);
+ ret = i2c_smbus_write_byte(client, STMFTS_SLEEP_OUT);
if (ret)
dev_err(dev, "failed to resume device: %d\n", ret);
--
2.53.0
^ permalink raw reply related
* [PATCH v4 05/11] Input: stmfts - disable regulators when power on fails
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
We must power off regulators after failing at power on phase.
Create stmfts_configure function, so we don't have to use goto.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 37 ++++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 71d9b747ccfc5..290511dd69437 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -539,22 +539,10 @@ static int stmfts_read_system_info(struct stmfts_data *sdata)
return 0;
}
-static int stmfts_power_on(struct stmfts_data *sdata)
+static int stmfts_configure(struct stmfts_data *sdata)
{
int err;
- err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies),
- sdata->supplies);
- if (err)
- return err;
-
- /*
- * The datasheet does not specify the power on time, but considering
- * that the reset time is < 10ms, I sleep 20ms to be sure
- */
- msleep(20);
-
-
err = stmfts_read_system_info(sdata);
if (err)
return err;
@@ -596,6 +584,29 @@ static int stmfts_power_on(struct stmfts_data *sdata)
return 0;
}
+static int stmfts_power_on(struct stmfts_data *sdata)
+{
+ int err;
+
+ err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies),
+ sdata->supplies);
+ if (err)
+ return err;
+
+ /*
+ * The datasheet does not specify the power on time, but considering
+ * that the reset time is < 10ms, I sleep 20ms to be sure
+ */
+ msleep(20);
+
+ err = stmfts_configure(sdata);
+ if (err)
+ regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
+ sdata->supplies);
+
+ return err;
+}
+
static void stmfts_power_off(void *data)
{
struct stmfts_data *sdata = data;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 01/11] Input: stmfts - Fix the MODULE_LICENSE() string
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Replace the bogus "GPL v2" with "GPL" as MODULE_LICNSE() string. The
value does not declare the module's exact license, but only lets the
module loader test whether the module is Free Software or not.
See commit bf7fbeeae6db ("module: Cure the MODULE_LICENSE "GPL" vs.
"GPL v2" bogosity") in the details of the issue. The fix is to use
"GPL" for all modules under any variant of the GPL.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 8af87d0b6eb64..def6bd0c8e059 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -807,4 +807,4 @@ module_i2c_driver(stmfts_driver);
MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH v4 04/11] Input: stmfts - abstract reading information from the firmware
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Improves readability and makes splitting power on function in following
commit easier.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 36 ++++++++++++++++++++++++------------
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index ff884e04ad4c8..71d9b747ccfc5 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -518,22 +518,11 @@ static struct attribute *stmfts_sysfs_attrs[] = {
};
ATTRIBUTE_GROUPS(stmfts_sysfs);
-static int stmfts_power_on(struct stmfts_data *sdata)
+static int stmfts_read_system_info(struct stmfts_data *sdata)
{
int err;
u8 reg[8];
- err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies),
- sdata->supplies);
- if (err)
- return err;
-
- /*
- * The datasheet does not specify the power on time, but considering
- * that the reset time is < 10ms, I sleep 20ms to be sure
- */
- msleep(20);
-
err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_INFO,
sizeof(reg), reg);
if (err < 0)
@@ -547,6 +536,29 @@ static int stmfts_power_on(struct stmfts_data *sdata)
sdata->config_id = reg[4];
sdata->config_ver = reg[5];
+ return 0;
+}
+
+static int stmfts_power_on(struct stmfts_data *sdata)
+{
+ int err;
+
+ err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies),
+ sdata->supplies);
+ if (err)
+ return err;
+
+ /*
+ * The datasheet does not specify the power on time, but considering
+ * that the reset time is < 10ms, I sleep 20ms to be sure
+ */
+ msleep(20);
+
+
+ err = stmfts_read_system_info(sdata);
+ if (err)
+ return err;
+
enable_irq(sdata->client->irq);
msleep(50);
--
2.53.0
^ permalink raw reply related
* [PATCH v4 03/11] Input: stmfts - Switch to devm_regulator_bulk_get_const
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg
In-Reply-To: <20260409-stmfts5-v4-0-64fe62027db5@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Switch to devm_regulator_bulk_get_const() to stop setting the supplies
list in probe(), and move the regulator_bulk_data struct in static const.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/touchscreen/stmfts.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index 7b1e975a85668..ff884e04ad4c8 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -69,9 +69,9 @@
#define STMFTS_MAX_FINGERS 10
#define STMFTS_DEV_NAME "stmfts"
-enum stmfts_regulators {
- STMFTS_REGULATOR_VDD,
- STMFTS_REGULATOR_AVDD,
+static const struct regulator_bulk_data stmfts_supplies[] = {
+ { .supply = "vdd" },
+ { .supply = "avdd" },
};
struct stmfts_data {
@@ -82,7 +82,7 @@ struct stmfts_data {
struct touchscreen_properties prop;
- struct regulator_bulk_data regulators[2];
+ struct regulator_bulk_data *supplies;
/*
* Presence of ledvdd will be used also to check
@@ -523,8 +523,8 @@ static int stmfts_power_on(struct stmfts_data *sdata)
int err;
u8 reg[8];
- err = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators),
- sdata->regulators);
+ err = regulator_bulk_enable(ARRAY_SIZE(stmfts_supplies),
+ sdata->supplies);
if (err)
return err;
@@ -589,8 +589,8 @@ static void stmfts_power_off(void *data)
struct stmfts_data *sdata = data;
disable_irq(sdata->client->irq);
- regulator_bulk_disable(ARRAY_SIZE(sdata->regulators),
- sdata->regulators);
+ regulator_bulk_disable(ARRAY_SIZE(stmfts_supplies),
+ sdata->supplies);
}
static int stmfts_enable_led(struct stmfts_data *sdata)
@@ -638,11 +638,10 @@ static int stmfts_probe(struct i2c_client *client)
mutex_init(&sdata->mutex);
init_completion(&sdata->cmd_done);
- sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd";
- sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd";
- err = devm_regulator_bulk_get(dev,
- ARRAY_SIZE(sdata->regulators),
- sdata->regulators);
+ err = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(stmfts_supplies),
+ stmfts_supplies,
+ &sdata->supplies);
if (err)
return err;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 00/11] Input: support for STM FTS5
From: David Heidelberg via B4 Relay @ 2026-04-08 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel, David Heidelberg, Konrad Dybcio
Used on various phones. Minimal viable driver.
Includes device-tree enabling touchscreen on Pixel 3.
What is missing:
- switching between AP and SLPI mode (to be able to wake up phone by touch)
- firmware loading
- anything above basic touch
Signed-off-by: David Heidelberg <david@ixit.cz>
---
Changes in v4:
- Wrap everything below enabling the supplies into stmfts_configure()
to avoid bunch of gotos to power off on error (Dmitry T.)
- Finished chip specific ops and removed is_fts5. (Dmitry T.)
- Link to v3: https://lore.kernel.org/r/20260403-stmfts5-v3-0-5da768cfd201@ixit.cz
Changes in v3:
- s/touchscreen_pins/touchscreen_irq_n. (Konrad)
- Use interrupts-extended. (Konrad)
- Fixed rebase conflict against 8665ceb926ec ("Input: stmfts - use guard notation when acquiring mutex")
- Rename switch-gpios to mode-switch-gpios.
- Do not define properties in if:then: branches. (Krzysztof)
- Link to v2: https://lore.kernel.org/r/20260315-stmfts5-v2-0-70bc83ee9591@ixit.cz
Changes in v2:
- Fix typo in the binding s/switch-gpio/switch-gpios/.
- Deduplacate allOf. (Rob yamllint)
- Add missing S-off-by. (Dmitry B.)
- Dropped irq-gpios as it's not needed. (Konrad)
- Correct x and y touchscreen area size. (Konrad)
- Correct reset introduction commit description. (Krzysztof)
- Partially implemented chip specific ops. (Dmitry T.)
- Separeted license naming cleanup into separate commit (Dmitry T.)
- Link to v1: https://lore.kernel.org/r/20260301-stmfts5-v1-0-22c458b9ac68@ixit.cz
---
David Heidelberg (7):
Input: stmfts - Fix the MODULE_LICENSE() string
Input: stmfts - Use dev struct directly
Input: stmfts - Switch to devm_regulator_bulk_get_const
Input: stmfts - abstract reading information from the firmware
Input: stmfts - disable regulators when power on fails
dt-bindings: input: touchscreen: st,stmfts: Introduce reset GPIO
dt-bindings: input: touchscreen: st,stmfts: Introduce STM FTS5
Petr Hodina (4):
Input: stmfts - use client to make future code cleaner
Input: stmfts - add optional reset GPIO support
Input: stmfts - support FTS5
arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support
.../bindings/input/touchscreen/st,stmfts.yaml | 19 +-
.../arm64/boot/dts/qcom/sdm845-google-blueline.dts | 19 +-
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 2 +-
drivers/input/touchscreen/stmfts.c | 601 +++++++++++++++++++--
4 files changed, 580 insertions(+), 61 deletions(-)
---
base-commit: db7efce4ae23ad5e42f5f55428f529ff62b86fab
change-id: 20260214-stmfts5-b47311fbd732
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply
* Re: [RFC PATCH 7/8] mm/vmalloc: Coalesce same page_shift mappings in vmap to avoid pgtable zigzag
From: Barry Song @ 2026-04-08 21:58 UTC (permalink / raw)
To: Dev Jain
Cc: linux-mm, linux-arm-kernel, catalin.marinas, will, akpm, urezki,
linux-kernel, anshuman.khandual, ryan.roberts, ajd, rppt, david,
Xueyuan.chen21
In-Reply-To: <90cfbbb4-9f75-4c67-a1c5-34780b2f7108@arm.com>
On Wed, Apr 8, 2026 at 7:36 PM Dev Jain <dev.jain@arm.com> wrote:
>
>
>
> On 08/04/26 8:21 am, Barry Song (Xiaomi) wrote:
> > For vmap(), detect pages with the same page_shift and map them in
> > batches, avoiding the pgtable zigzag caused by per-page mapping.
> >
> > Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
> > ---
>
> In patch 4, you eliminate the pagetable rewalk, and in patch 5,
> you re-introduce it, then in this patch you eliminate it again.
> So please just squash this into #5.
>
Sure. I guess I revealed some of the development process in this
patchset. The patch was initially written with page table rewalks
for simplicity and then iteratively improved. I agree that nobody
really cares how it was originally written :-)
Best Regards
Barry
^ permalink raw reply
* Re: [RFC PATCH 5/8] mm/vmalloc: map contiguous pages in batches for vmap() if possible
From: Barry Song @ 2026-04-08 21:54 UTC (permalink / raw)
To: Dev Jain
Cc: linux-mm, linux-arm-kernel, catalin.marinas, will, akpm, urezki,
linux-kernel, anshuman.khandual, ryan.roberts, ajd, rppt, david,
Xueyuan.chen21
In-Reply-To: <c54e12b0-1a35-48e1-b243-991de246a8ca@arm.com>
On Wed, Apr 8, 2026 at 10:03 PM Dev Jain <dev.jain@arm.com> wrote:
>
>
>
> On 08/04/26 8:21 am, Barry Song (Xiaomi) wrote:
> > In many cases, the pages passed to vmap() may include high-order
> > pages allocated with __GFP_COMP flags. For example, the systemheap
> > often allocates pages in descending order: order 8, then 4, then 0.
> > Currently, vmap() iterates over every page individually—even pages
> > inside a high-order block are handled one by one.
> >
> > This patch detects high-order pages and maps them as a single
> > contiguous block whenever possible.
> >
> > An alternative would be to implement a new API, vmap_sg(), but that
> > change seems to be large in scope.
> >
> > Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
> > ---
> > mm/vmalloc.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 49 insertions(+), 2 deletions(-)
> >
> > diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> > index eba436386929..e8dbfada42bc 100644
> > --- a/mm/vmalloc.c
> > +++ b/mm/vmalloc.c
> > @@ -3529,6 +3529,53 @@ void vunmap(const void *addr)
> > }
> > EXPORT_SYMBOL(vunmap);
> >
> > +static inline int get_vmap_batch_order(struct page **pages,
> > + unsigned int max_steps, unsigned int idx)
> > +{
> > + unsigned int nr_pages;
> > +
> > + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP) ||
> > + ioremap_max_page_shift == PAGE_SHIFT)
> > + return 0;
> > +
> > + nr_pages = compound_nr(pages[idx]);
> > + if (nr_pages == 1 || max_steps < nr_pages)
> > + return 0;
>
> This assumes that the page array passed to vmap() will have compound pages
> if it is a higher order allocation.
>
> See rb_alloc_aux_page(). It gets higher-order allocations without passing
> GFP_COMP.
>
> That is why my implementation does not assume anything about the property
> of the pages.
If you’re asking about support for non-compound pages, I think
that’s fine. My current use case is dma-buf, where pages are
compound. I recall discussing this previously with David and
Uladzislau.
If you’re working with non-compound pages, I’m happy to add
support in the next version. I’m also happy to reuse some of your
code and credit you as Co-developed-by if you’re willing. I actually
prefer your __vmap_huge() name over my
vmap_contig_pages_range().
Does that make sense to you?
>
> Also it may be useful to do regression-testing for the common case of
> vmap() with a single page (assuming it is common, I don't know), in
> which case we may have to special case it.
I agree, so I had Xueyuan test single pages and highlighted this
in the cover letter. There is no regression: "vmap() is 5.6×
faster when memory includes some order-8 pages, with no
regression observed for order-0 pages."
>
> My implementation requires opting in with VM_ALLOW_HUGE_VMAP - I suspect
> you may run into problems if you make vmap() do huge-mappings as best-effort
> by default. I am guessing this because ...
>
> Drivers can operate on individual pages, so vmalloc() calls split_page()
> and then does the block/cont mappings. This same issue should be present
> with vmap() too? In which case if we are to do huge-mappings by default
> then we can do split_page() after detecting contiguous chunks.
>
> But ... that may create problems for the caller of vmap() - vmap now
> has the changed the properties of the pages.
I don’t see this as a problem at all. Splitting pages does not
affect physical or virtual contiguity; it only changes the
contents of struct page objects, not the PTE/PMD mappings.
For ioremap, there isn’t even a struct page, yet the mappings
can still be huge.
Thanks
Barry
^ permalink raw reply
* Re: [PATCH v3 3/3] dt-bindings: i3c: Add AST2600 I3C global registers
From: Rob Herring (Arm) @ 2026-04-08 21:35 UTC (permalink / raw)
To: Dawid Glazik
Cc: Andrew Jeffery, Krzysztof Kozlowski, Joel Stanley,
linux-arm-kernel, Frank Li, Conor Dooley, linux-i3c,
Maciej Lawniczak, Alexandre Belloni, linux-aspeed, devicetree
In-Reply-To: <7f55458097ef651b4fc46650254afd3fa7b87348.1775679285.git.dawid.glazik@linux.intel.com>
On Wed, 08 Apr 2026 22:34:35 +0200, Dawid Glazik wrote:
> Introduce the device-tree bindings for I3C global registers found on
> AST2600 SoCs.
>
> Signed-off-by: Dawid Glazik <dawid.glazik@linux.intel.com>
> ---
> I wasn't sure if I should add newline at the end of the
> file or not so I took
> https://github.com/torvalds/linux/tree/master/Documentation/devicetree/bindings/i3c
> as an example.
> ---
> .../i3c/aspeed,ast2600-i3c-global.yaml | 55 +++++++++++++++++++
> 1 file changed, 55 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c-global.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c-global.yaml:55:4: [error] no new line character at the end of file (new-line-at-end-of-file)
dtschema/dtc warnings/errors:
Lexical error: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c-global.example.dts:27.35-55 Unexpected 'ASPEED_RESET_I3C_DMA'
FATAL ERROR: Syntax error parsing input tree
make[2]: *** [scripts/Makefile.dtbs:140: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c-global.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1597: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/7f55458097ef651b4fc46650254afd3fa7b87348.1775679285.git.dawid.glazik@linux.intel.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* [GIT PULL] coresight: Updates for Linux v7.1
From: Suzuki K Poulose @ 2026-04-08 21:32 UTC (permalink / raw)
To: gregkh
Cc: leo.yan, mike.leach, linux-arm-kernel, linux-kernel, james.clark,
suzuki.poulose
Hi Greg
Please find the updates for CoreSight self-hosted tracing subsystem targeting
v7.1
Kindly pull,
Suzuki
The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git tags/coresight-next-v7.1
for you to fetch changes up to 971f3474f8898ae8bbab19a9b547819a5e6fbcf1:
coresight: tpdm: fix invalid MMIO access issue (2026-04-07 12:29:42 +0100)
----------------------------------------------------------------
coresight: Updates for Linux v7.1
CoreSight self hosted tracing subsystem updates for Linux v7.1, includes:
- Fix unregistration related issues
- Clean up CTI power management and sysfs code
- Miscellaneous fixes
- MAINTAINERS: Add Leo Yan as Reviewer
- MAINTAINERS: Update Mike's email address
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
----------------------------------------------------------------
Elsanti (1):
drivers/hwtracing/coresight: remove unneeded variable in tmc_crashdata_release()
Jie Gan (5):
coresight: ctcu: fix the spin_bug
coresight: cti: fix the check condition in inout_sel_store
coresight: platform: check the availability of the endpoint before parse
coresight: tpdm: add traceid_show for checking traceid
coresight: tpdm: fix invalid MMIO access issue
Leo Yan (17):
coresight: cti: Move resource release to cti_remove()
coresight: Fix memory leak in coresight_alloc_device_name()
coresight: Get parent device reference after sink ID map allocation
coresight: Protect unregistration with mutex
coresight: Refactor output connection sysfs link cleanup
coresight: Refactor sysfs connection group cleanup
coresight: Move sink validation into etm_perf_add_symlink_sink()
coresight: Do not mix success path with failure handling
coresight: Unify bus unregistration via coresight_unregister()
coresight: cti: Make spinlock usage consistent
coresight: cti: Fix register reads
coresight: cti: Access ASICCTL only when implemented
coresight: cti: Remove CPU power management code
coresight: cti: Rename cti_active() to cti_is_active()
coresight: cti: Remove hw_powered flag
coresight: cti: Remove hw_enabled flag
coresight: cti: Properly handle negative offsets in cti_reg32_{show|store}()
Mike Leach (1):
MAINTAINERS: Change e-mail address for reviewer
Suzuki K Poulose (1):
MAINTAINERS: coresight: Add Leo Yan as Reviewer
.mailmap | 1 +
.../ABI/testing/sysfs-bus-coresight-devices-tpdm | 10 +
MAINTAINERS | 5 +-
drivers/hwtracing/coresight/coresight-catu.c | 4 +-
drivers/hwtracing/coresight/coresight-core.c | 214 ++++++++------
drivers/hwtracing/coresight/coresight-ctcu-core.c | 5 +-
drivers/hwtracing/coresight/coresight-cti-core.c | 321 ++++-----------------
drivers/hwtracing/coresight/coresight-cti-sysfs.c | 174 +++++------
drivers/hwtracing/coresight/coresight-cti.h | 15 +-
drivers/hwtracing/coresight/coresight-dummy.c | 7 +-
drivers/hwtracing/coresight/coresight-etb10.c | 4 +-
drivers/hwtracing/coresight/coresight-etm-perf.c | 5 +-
drivers/hwtracing/coresight/coresight-funnel.c | 4 +-
drivers/hwtracing/coresight/coresight-platform.c | 4 +-
drivers/hwtracing/coresight/coresight-priv.h | 3 +-
drivers/hwtracing/coresight/coresight-replicator.c | 4 +-
drivers/hwtracing/coresight/coresight-stm.c | 4 +-
drivers/hwtracing/coresight/coresight-tmc-core.c | 15 +-
drivers/hwtracing/coresight/coresight-tnoc.c | 4 +-
drivers/hwtracing/coresight/coresight-tpda.c | 4 +-
drivers/hwtracing/coresight/coresight-tpdm.c | 40 ++-
drivers/hwtracing/coresight/coresight-tpdm.h | 2 +
drivers/hwtracing/coresight/coresight-tpiu.c | 4 +-
drivers/hwtracing/coresight/ultrasoc-smb.c | 4 +-
include/linux/coresight.h | 14 +-
25 files changed, 363 insertions(+), 508 deletions(-)
^ permalink raw reply
* Re: [RFC PATCH 3/8] mm/vmalloc: Extend vmap_small_pages_range_noflush() to support larger page_shift sizes
From: Barry Song @ 2026-04-08 21:29 UTC (permalink / raw)
To: Dev Jain
Cc: linux-mm, linux-arm-kernel, catalin.marinas, will, akpm, urezki,
linux-kernel, anshuman.khandual, ryan.roberts, ajd, rppt, david,
Xueyuan.chen21
In-Reply-To: <f661064f-269c-4e1d-8d6c-a9a2e50cd82c@arm.com>
On Wed, Apr 8, 2026 at 7:08 PM Dev Jain <dev.jain@arm.com> wrote:
>
>
>
> On 08/04/26 8:21 am, Barry Song (Xiaomi) wrote:
> > vmap_small_pages_range_noflush() provides a clean interface by taking
> > struct page **pages and mapping them via direct PTE iteration. This
> > avoids the page table zigzag seen when using
>
> "Zigzag" is ambiguous. Just say "page table rewalk". Also please
> elaborate on why the rewalk is happening currently.
Sure—I’m not a native speaker, but “zigzag” feels like
“repeatedly walking the page tables back and forth” to me.
That’s exactly what I want to express.
>
> > vmap_range_noflush() for page_shift values other than PAGE_SHIFT.
> >
> > Extend it to support larger page_shift values, and add PMD- and
> > contiguous-PTE mappings as well.
>
> So we can drop the "small" here since now it supports larger chunks
> as well.
I’m still struggling with the name, since we already have a
function without “small” here:
int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
pgprot_t prot, struct page **pages, unsigned int page_shift,
gfp_t gfp_mask)
>
> Also at this point the code you add is a no-op since you pass PAGE_SHIFT.
> Let us just squash patch 4 into this. This patch looks weird retaining
> the pagetable-rewalk algorithm when it literally adds functionality
> to avoid that.
Sure. will do.
Thanks
Barry
^ permalink raw reply
* Re: [PATCH 0/4] arm64: mitigate CVE-2024-7881 in the absence of firmware mitigation
From: David Woodhouse @ 2026-04-08 21:26 UTC (permalink / raw)
To: will
Cc: catalin.marinas, joey.gouly, kvmarm, linux-arm-kernel,
mark.rutland, maz, oliver.upton, suzuki.poulose, yuzenghui
In-Reply-To: <20250317212611.GA12724@willie-the-truck>
[-- Attachment #1: Type: text/plain, Size: 648 bytes --]
On Mon, 17 Mar 2025 at 21:26:12 +0000, Will Deacon wrote:
> I'm really not comfortable with this series and would prefer to see it
> dropped while we continue the discussion, especially as it's causing
> minor conflicts with the KVM/arm64 tree in -next.
>
> ...
>
> To be clear: I'm not at all against mitigating this problem and
> advertising the status of that mitigation. I *am* against quietly
> handling it like a CPU erratum whilst simultaneously telling userspace
> that meltdown is not a problem regardless of the mitigation state.
Was there a conclusion? KVM still isn't even exposing
SMCCC_ARCH_WORKAROUND_4 to guests...
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [RFC PATCH 1/7] media: v4l2-ctrls: Add V4L2_CID_MEMORY_USAGE control
From: Nicolas Dufresne @ 2026-04-08 21:11 UTC (permalink / raw)
To: Ming Qian(OSS), Frank Li, Detlev Casanova
Cc: linux-media, mchehab, hverkuil-cisco, sebastian.fricke, shawnguo,
s.hauer, kernel, festevam, linux-imx, xiahong.bao, eagle.zhou,
imx, linux-kernel, linux-arm-kernel
In-Reply-To: <5c24fe3f-a1c7-4fd6-b5e6-c920bc3e7fcb@oss.nxp.com>
[-- Attachment #1: Type: text/plain, Size: 5754 bytes --]
Le jeudi 02 avril 2026 à 11:14 +0800, Ming Qian(OSS) a écrit :
> Hi Nicolas,
>
> On 4/1/2026 10:23 AM, Ming Qian(OSS) wrote:
> > Hi Nicolas,
> >
> > On 3/31/2026 10:54 PM, Nicolas Dufresne wrote:
> > > Le mardi 31 mars 2026 à 10:33 -0400, Frank Li a écrit :
> > > > On Tue, Mar 31, 2026 at 03:23:11PM +0800, ming.qian@oss.nxp.com wrote:
> > > > > From: Ming Qian <ming.qian@oss.nxp.com>
> > > > >
> > > > > Add a new read-only control V4L2_CID_MEMORY_USAGE that allows
> > > > > applications to query the total amount of memory currently used
> > > > > by a device instance.
> > > > >
> > > > > This control reports the memory consumption in bytes, including
> > > > > internal buffers, intermediate processing data, and other
> > > > > driver-managed allocations. Applications can use this information
> > > > > for debugging, resource monitoring, or making informed decisions
> > > > > about buffer allocation strategies.
> > > > >
> > > > > Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
> > > > > ---
> > > >
> > > > Not sure why not export these information by debugfs, or any benefit vs
> > > > debugfs?
> > >
> > > There is also a on-going proposal that uses fdinfo.
> > >
> > > Nicolas
> > >
> >
> > Thanks for the reminder about the ongoing fdinfo proposal.
> >
> > Just to confirm, you are referring to Detlev’s ongoing fdinfo proposal,
> > specifically this series:
> > https://lore.kernel.org/lkml/20260212162328.192217-1-
> > detlev.casanova@collabora.com/
> >
> > I will align my work with it and switch to using fdinfo.
> > Once the show_fdinfo support from that series is merged, I will prepare
> > the next revision of my patch accordingly.
> >
> > Regards,
> > Ming
> >
>
> Regarding the discussion about using fdinfo instead of a V4L2 control, I
> have two questions:
>
> 1. Key consistency in fdinfo
> fdinfo uses key–value pairs, which is flexible, but if multiple
> drivers want to expose the same “memory usage” information,
> they need to agree on a common key name and meaning. Otherwise
> user‑space must handle each driver differently. A V4L2 control
> naturally provides a unified interface without this coordination
> effort.
>
>
> 2. Lack of notification in fdinfo
> With a control, user‑space can subscribe to control events and
> receive notifications when the memory usage changes. fdinfo does
> not have a built‑in event mechanism, so users must either poll
> or rely on additional eventfd‑like or custom event mechanisms.
>
> Do you have any suggestions or existing practices to address these two
> issues when using fdinfo?
>
> Thanks again for your time and comments.
Added Detlev in CC. You can also refer to his work through:
https://lore.kernel.org/all/20260212162328.192217-1-detlev.casanova@collabora.com/
Nicolas
>
> Regards,
> Ming
>
> > > >
> > > > Generanlly document should be first patch, then driver change.
> > > >
> > > > Frank
> > > >
> > > > > drivers/media/v4l2-core/v4l2-ctrls-defs.c | 8 ++++++++
> > > > > include/uapi/linux/v4l2-controls.h | 4 +++-
> > > > > 2 files changed, 11 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/
> > > > > media/v4l2-core/v4l2-ctrls-defs.c
> > > > > index 551426c4cd01..053db78ff661 100644
> > > > > --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
> > > > > +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
> > > > > @@ -831,6 +831,7 @@ const char *v4l2_ctrl_get_name(u32 id)
> > > > > case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component";
> > > > > case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr";
> > > > > case V4L2_CID_COLORFX_RGB: return "Color Effects,
> > > > > RGB";
> > > > > + case V4L2_CID_MEMORY_USAGE: return "Memory Usage";
> > > > >
> > > > > /*
> > > > > * Codec controls
> > > > > @@ -1476,6 +1477,13 @@ void v4l2_ctrl_fill(u32 id, const char
> > > > > **name, enum v4l2_ctrl_type *type,
> > > > > *min = 0;
> > > > > *max = 0xffff;
> > > > > break;
> > > > > + case V4L2_CID_MEMORY_USAGE:
> > > > > + *type = V4L2_CTRL_TYPE_INTEGER64;
> > > > > + *flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > > > > + *min = 0;
> > > > > + *max = S64_MAX;
> > > > > + *step = 1;
> > > > > + break;
> > > > > case V4L2_CID_FLASH_FAULT:
> > > > > case V4L2_CID_JPEG_ACTIVE_MARKER:
> > > > > case V4L2_CID_3A_LOCK:
> > > > > diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/
> > > > > linux/v4l2-controls.h
> > > > > index 68dd0c4e47b2..02c6f960d38e 100644
> > > > > --- a/include/uapi/linux/v4l2-controls.h
> > > > > +++ b/include/uapi/linux/v4l2-controls.h
> > > > > @@ -110,8 +110,10 @@ enum v4l2_colorfx {
> > > > > #define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42)
> > > > > #define V4L2_CID_COLORFX_RGB (V4L2_CID_BASE+43)
> > > > >
> > > > > +#define V4L2_CID_MEMORY_USAGE (V4L2_CID_BASE+44)
> > > > > +
> > > > > /* last CID + 1 */
> > > > > -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+44)
> > > > > +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+45)
> > > > >
> > > > > /* USER-class private control IDs */
> > > > >
> > > > > --
> > > > > 2.53.0
> > > > >
> >
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH] media: mtk-jpeg: cancel workqueue on release for supported platforms only
From: Nicolas Dufresne @ 2026-04-08 21:03 UTC (permalink / raw)
To: Louis-Alexis Eyraud, Bin Liu, Mauro Carvalho Chehab,
Matthias Brugger, AngeloGioacchino Del Regno, Hans Verkuil,
Fan Wu
Cc: kernel, linux-media, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260401-mtk-jpeg-release-issue-v1-1-2271a1779340@collabora.com>
[-- Attachment #1: Type: text/plain, Size: 3043 bytes --]
Le mercredi 01 avril 2026 à 11:44 +0200, Louis-Alexis Eyraud a écrit :
> Since a recent fix the mtk_jpeg_release function cancels any pending
> or running work present in the driver workqueue using
> cancel_work_sync function.
> Currently, only the multicore based variants use this workqueue and they
> have the jpeg_worker platform data field initialized with a workqueue
> callback function. For the others, this field value remain NULL by
> default.
> The cancel_work_sync function is unconditionally called in
> mtk_jpeg_release function, even for the variants that do not use the
> workqueue. This call generates a WARN_ON print in __flush_work because
> the workqueue callback function presence check fails in __flush_work
> function (used by cancel_work_sync).
>
> So, to avoid these warnings, call cancel_work_sync only if a workqueue
> callback is defined in platform data.
>
> Fixes: 34c519feef3e ("media: mtk-jpeg: fix use-after-free in release path due to uncancelled work")
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> ---
> Patch to fix the following warning that occurs on boards such as
> Mediatek Genio 510-EVK:
> ```
> WARNING: kernel/workqueue.c:4291 at __flush_work+0x320/0x340, CPU#1:
> v4l_id/409
> ...
> Call trace:
> __flush_work+0x320/0x340 (P)
> cancel_work_sync+0x6c/0x9c
> mtk_jpeg_release+0x2c/0x84 [mtk_jpeg]
> v4l2_release+0x8c/0xe8 [videodev]
> __fput+0xcc/0x2e0
> fput_close_sync+0x40/0x100
> __arm64_sys_close+0x38/0x7c
> invoke_syscall+0x54/0x10c
> el0_svc_common.constprop.0+0xc0/0xe0
> do_el0_svc+0x1c/0x34
> el0_svc+0x34/0x108
> el0t_64_sync_handler+0xa0/0xf0
> el0t_64_sync+0x198/0x19c
> ---[ end trace 0000000000000000 ]---
> ```
>
> It is based on linux-next (tag next-20260331) and has been tested on
> Mediatek Genio 510-EVK (mt8390) and 1200-EVK (mt8395) boards.
> ---
> drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> index 8c684756d5fc2524da3a67f67f0fdda894b676fc..d147ec48308110ae8520662e182dc0445447d8d0 100644
> --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> @@ -1202,7 +1202,8 @@ static int mtk_jpeg_release(struct file *file)
> struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file);
>
> - cancel_work_sync(&ctx->jpeg_work);
> + if (jpeg->variant->jpeg_worker)
> + cancel_work_sync(&ctx->jpeg_work);
> mutex_lock(&jpeg->lock);
> v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
>
> ---
> base-commit: e5da3eef8dadab4e98b228725ca8948edd9d601f
> change-id: 20260401-mtk-jpeg-release-issue-d8921970a250
>
> Best regards,
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v2] media: verisilicon: Simplify motion vectors and rfc buffers allocation
From: Nicolas Dufresne @ 2026-04-08 20:41 UTC (permalink / raw)
To: Benjamin Gaignard, p.zabel, mchehab, heiko
Cc: linux-media, linux-rockchip, linux-kernel, linux-arm-kernel,
kernel
In-Reply-To: <20260325131727.13575-1-benjamin.gaignard@collabora.com>
[-- Attachment #1: Type: text/plain, Size: 35304 bytes --]
Hi,
Le mercredi 25 mars 2026 à 14:17 +0100, Benjamin Gaignard a écrit :
> Until now we reserve the space needed for motion vectors and reference
> frame compression at the end of the frame buffer.
> This patch disentanglement mv and rfc from frame buffers by allocating
Use imperative tone, avoid sarting a story (Once upon a time ...), drop "This patch", we know its a patch.
> distinct buffers for each purpose.
> That simplify the code by removing lot of offset computation.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
> ---
> version 2:
> - rework commit message
> - free mv and rfc buffer before signal the buffer completion.
>
> drivers/media/platform/verisilicon/hantro.h | 17 +-
> .../media/platform/verisilicon/hantro_av1.c | 7 -
> .../media/platform/verisilicon/hantro_av1.h | 1 -
> .../media/platform/verisilicon/hantro_g2.c | 36 --
> .../platform/verisilicon/hantro_g2_hevc_dec.c | 24 +-
> .../platform/verisilicon/hantro_g2_vp9_dec.c | 12 +-
> .../media/platform/verisilicon/hantro_hevc.c | 20 +-
> .../media/platform/verisilicon/hantro_hw.h | 99 +-----
> .../platform/verisilicon/hantro_postproc.c | 29 +-
> .../media/platform/verisilicon/hantro_v4l2.c | 314 ++++++++++++++++--
> .../verisilicon/rockchip_vpu981_hw_av1_dec.c | 16 +-
> 11 files changed, 359 insertions(+), 216 deletions(-)
>
> diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h
> index 0353de154a1e..c4ceb9c99016 100644
> --- a/drivers/media/platform/verisilicon/hantro.h
> +++ b/drivers/media/platform/verisilicon/hantro.h
> @@ -31,6 +31,9 @@ struct hantro_ctx;
> struct hantro_codec_ops;
> struct hantro_postproc_ops;
>
> +#define MAX_MV_BUFFERS MAX_POSTPROC_BUFFERS
> +#define MAX_RFC_BUFFERS MAX_POSTPROC_BUFFERS
Why two defines ? And why 64 ? Isn't the maximum something per codec ?
> +
> #define HANTRO_JPEG_ENCODER BIT(0)
> #define HANTRO_ENCODERS 0x0000ffff
> #define HANTRO_MPEG2_DECODER BIT(16)
> @@ -237,6 +240,9 @@ struct hantro_dev {
> * @need_postproc: Set to true if the bitstream features require to
> * use the post-processor.
> *
> + * @dec_mv: motion vectors buffers for the context.
> + * @dec_rfc: reference frame compression buffers for the context.
> + *
> * @codec_ops: Set of operations related to codec mode.
> * @postproc: Post-processing context.
> * @h264_dec: H.264-decoding context.
> @@ -264,6 +270,9 @@ struct hantro_ctx {
> int jpeg_quality;
> int bit_depth;
>
> + struct hantro_aux_buf dec_mv[MAX_MV_BUFFERS];
> + struct hantro_aux_buf dec_rfc[MAX_RFC_BUFFERS];
> +
> const struct hantro_codec_ops *codec_ops;
> struct hantro_postproc_ctx postproc;
> bool need_postproc;
> @@ -334,14 +343,14 @@ struct hantro_vp9_decoded_buffer_info {
> unsigned short width;
> unsigned short height;
> size_t chroma_offset;
> - size_t mv_offset;
> + dma_addr_t mv_addr;
> u32 bit_depth : 4;
> };
>
> struct hantro_av1_decoded_buffer_info {
> /* Info needed when the decoded frame serves as a reference frame. */
> size_t chroma_offset;
> - size_t mv_offset;
> + dma_addr_t mv_addr;
> };
>
> struct hantro_decoded_buffer {
> @@ -507,4 +516,8 @@ void hantro_postproc_free(struct hantro_ctx *ctx);
> int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx,
> struct v4l2_frmsizeenum *fsize);
>
> +dma_addr_t hantro_mv_get_buf_addr(struct hantro_ctx *ctx, int index);
> +dma_addr_t hantro_rfc_get_luma_buf_addr(struct hantro_ctx *ctx, int index);
> +dma_addr_t hantro_rfc_get_chroma_buf_addr(struct hantro_ctx *ctx, int index);
> +
> #endif /* HANTRO_H_ */
> diff --git a/drivers/media/platform/verisilicon/hantro_av1.c b/drivers/media/platform/verisilicon/hantro_av1.c
> index 5a51ac877c9c..3a80a7994f67 100644
> --- a/drivers/media/platform/verisilicon/hantro_av1.c
> +++ b/drivers/media/platform/verisilicon/hantro_av1.c
> @@ -222,13 +222,6 @@ size_t hantro_av1_luma_size(struct hantro_ctx *ctx)
> return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height;
> }
>
> -size_t hantro_av1_chroma_size(struct hantro_ctx *ctx)
> -{
> - size_t cr_offset = hantro_av1_luma_size(ctx);
> -
> - return ALIGN((cr_offset * 3) / 2, 64);
> -}
> -
> static void hantro_av1_tiles_free(struct hantro_ctx *ctx)
> {
> struct hantro_dev *vpu = ctx->dev;
> diff --git a/drivers/media/platform/verisilicon/hantro_av1.h b/drivers/media/platform/verisilicon/hantro_av1.h
> index 4e2122b95cdd..330f7938d097 100644
> --- a/drivers/media/platform/verisilicon/hantro_av1.h
> +++ b/drivers/media/platform/verisilicon/hantro_av1.h
> @@ -41,7 +41,6 @@ int hantro_av1_get_order_hint(struct hantro_ctx *ctx, int ref);
> int hantro_av1_frame_ref(struct hantro_ctx *ctx, u64 timestamp);
> void hantro_av1_clean_refs(struct hantro_ctx *ctx);
> size_t hantro_av1_luma_size(struct hantro_ctx *ctx);
> -size_t hantro_av1_chroma_size(struct hantro_ctx *ctx);
> void hantro_av1_exit(struct hantro_ctx *ctx);
> int hantro_av1_init(struct hantro_ctx *ctx);
> int hantro_av1_prepare_run(struct hantro_ctx *ctx);
> diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c
> index 318673b66da8..4ae7df53dcb1 100644
> --- a/drivers/media/platform/verisilicon/hantro_g2.c
> +++ b/drivers/media/platform/verisilicon/hantro_g2.c
> @@ -99,39 +99,3 @@ size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx)
> {
> return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height;
> }
> -
> -size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx)
> -{
> - size_t cr_offset = hantro_g2_chroma_offset(ctx);
> -
> - return ALIGN((cr_offset * 3) / 2, G2_ALIGN);
> -}
> -
> -static size_t hantro_g2_mv_size(struct hantro_ctx *ctx)
> -{
> - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
> - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
> - unsigned int pic_width_in_ctbs, pic_height_in_ctbs;
> - unsigned int max_log2_ctb_size;
> -
> - max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 +
> - sps->log2_diff_max_min_luma_coding_block_size;
> - pic_width_in_ctbs = (sps->pic_width_in_luma_samples +
> - (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size;
> - pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1)
> - >> max_log2_ctb_size;
> -
> - return pic_width_in_ctbs * pic_height_in_ctbs * (1 << (2 * (max_log2_ctb_size - 4))) * 16;
> -}
> -
> -size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx)
> -{
> - return hantro_g2_motion_vectors_offset(ctx) +
> - hantro_g2_mv_size(ctx);
> -}
> -
> -size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx)
> -{
> - return hantro_g2_luma_compress_offset(ctx) +
> - hantro_hevc_luma_compressed_size(ctx->dst_fmt.width, ctx->dst_fmt.height);
> -}
> diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
> index e8c2e83379de..d0af9fb882ba 100644
> --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
> +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
> @@ -383,9 +383,6 @@ static int set_ref(struct hantro_ctx *ctx)
> struct vb2_v4l2_buffer *vb2_dst;
> struct hantro_decoded_buffer *dst;
> size_t cr_offset = hantro_g2_chroma_offset(ctx);
> - size_t mv_offset = hantro_g2_motion_vectors_offset(ctx);
> - size_t compress_luma_offset = hantro_g2_luma_compress_offset(ctx);
> - size_t compress_chroma_offset = hantro_g2_chroma_compress_offset(ctx);
> u32 max_ref_frames;
> u16 dpb_longterm_e;
> static const struct hantro_reg cur_poc[] = {
> @@ -453,14 +450,17 @@ static int set_ref(struct hantro_ctx *ctx)
> dpb_longterm_e = 0;
> for (i = 0; i < decode_params->num_active_dpb_entries &&
> i < (V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1); i++) {
> + int index = hantro_hevc_get_ref_buf_index(ctx, dpb[i].pic_order_cnt_val);
> luma_addr = hantro_hevc_get_ref_buf(ctx, dpb[i].pic_order_cnt_val);
> if (!luma_addr)
> return -ENOMEM;
>
> chroma_addr = luma_addr + cr_offset;
> - mv_addr = luma_addr + mv_offset;
> - compress_luma_addr = luma_addr + compress_luma_offset;
> - compress_chroma_addr = luma_addr + compress_chroma_offset;
> + mv_addr = hantro_mv_get_buf_addr(ctx, index);
> + if (ctx->hevc_dec.use_compression) {
> + compress_luma_addr = hantro_rfc_get_luma_buf_addr(ctx, index);
> + compress_chroma_addr = hantro_rfc_get_chroma_buf_addr(ctx, index);
> + }
>
> if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE)
> dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i);
> @@ -478,13 +478,17 @@ static int set_ref(struct hantro_ctx *ctx)
> if (!luma_addr)
> return -ENOMEM;
>
> - if (hantro_hevc_add_ref_buf(ctx, decode_params->pic_order_cnt_val, luma_addr))
> + if (hantro_hevc_add_ref_buf(ctx, decode_params->pic_order_cnt_val, luma_addr, vb2_dst))
> return -EINVAL;
>
> chroma_addr = luma_addr + cr_offset;
> - mv_addr = luma_addr + mv_offset;
> - compress_luma_addr = luma_addr + compress_luma_offset;
> - compress_chroma_addr = luma_addr + compress_chroma_offset;
> + mv_addr = hantro_mv_get_buf_addr(ctx, dst->base.vb.vb2_buf.index);
> + if (ctx->hevc_dec.use_compression) {
> + compress_luma_addr =
> + hantro_rfc_get_luma_buf_addr(ctx, dst->base.vb.vb2_buf.index);
> + compress_chroma_addr =
> + hantro_rfc_get_chroma_buf_addr(ctx, dst->base.vb.vb2_buf.index);
> + }
>
> hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr);
> hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr);
> diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
> index 56c79e339030..1e96d0fce72a 100644
> --- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
> +++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
> @@ -129,7 +129,7 @@ static void config_output(struct hantro_ctx *ctx,
> struct hantro_decoded_buffer *dst,
> const struct v4l2_ctrl_vp9_frame *dec_params)
> {
> - dma_addr_t luma_addr, chroma_addr, mv_addr;
> + dma_addr_t luma_addr, chroma_addr;
>
> hantro_reg_write(ctx->dev, &g2_out_dis, 0);
> if (!ctx->dev->variant->legacy_regs)
> @@ -142,9 +142,8 @@ static void config_output(struct hantro_ctx *ctx,
> hantro_write_addr(ctx->dev, G2_OUT_CHROMA_ADDR, chroma_addr);
> dst->vp9.chroma_offset = hantro_g2_chroma_offset(ctx);
>
> - mv_addr = luma_addr + hantro_g2_motion_vectors_offset(ctx);
> - hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, mv_addr);
> - dst->vp9.mv_offset = hantro_g2_motion_vectors_offset(ctx);
> + dst->vp9.mv_addr = hantro_mv_get_buf_addr(ctx, dst->base.vb.vb2_buf.index);
> + hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, dst->vp9.mv_addr);
> }
>
> struct hantro_vp9_ref_reg {
> @@ -215,15 +214,12 @@ static void config_ref_registers(struct hantro_ctx *ctx,
> .c_base = G2_REF_CHROMA_ADDR(5),
> },
> };
> - dma_addr_t mv_addr;
>
> config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params->last_frame_ts);
> config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params->golden_frame_ts);
> config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params->alt_frame_ts);
>
> - mv_addr = hantro_get_dec_buf_addr(ctx, &mv_ref->base.vb.vb2_buf) +
> - mv_ref->vp9.mv_offset;
> - hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_addr);
> + hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_ref->vp9.mv_addr);
>
> hantro_reg_write(ctx->dev, &vp9_last_sign_bias,
> dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0);
> diff --git a/drivers/media/platform/verisilicon/hantro_hevc.c b/drivers/media/platform/verisilicon/hantro_hevc.c
> index 83cd12b0ddd6..272ce336b1c6 100644
> --- a/drivers/media/platform/verisilicon/hantro_hevc.c
> +++ b/drivers/media/platform/verisilicon/hantro_hevc.c
> @@ -54,7 +54,24 @@ dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx,
> return 0;
> }
>
> -int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr)
> +int hantro_hevc_get_ref_buf_index(struct hantro_ctx *ctx, s32 poc)
> +{
> + struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec;
> + int i;
> +
> + /* Find the reference buffer in already known ones */
> + for (i = 0; i < NUM_REF_PICTURES; i++) {
> + if (hevc_dec->ref_bufs_poc[i] == poc)
> + return hevc_dec->ref_vb2[i]->vb2_buf.index;
I'm a little worried that there is no flag indicating if the entry was set or
not. POC 0 is valid notably, do we initialize to an invalid value to prevent
matching an unset entry or unused entry ?
> + }
> +
> + return 0;
> +}
> +
> +int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx,
> + int poc,
> + dma_addr_t addr,
> + struct vb2_v4l2_buffer *vb2)
> {
> struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec;
> int i;
> @@ -65,6 +82,7 @@ int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr)
> hevc_dec->ref_bufs_used |= 1 << i;
> hevc_dec->ref_bufs_poc[i] = poc;
> hevc_dec->ref_bufs[i].dma = addr;
> + hevc_dec->ref_vb2[i] = vb2;
> return 0;
> }
> }
> diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h
> index f0e4bca4b2b2..6a1ee9899b60 100644
> --- a/drivers/media/platform/verisilicon/hantro_hw.h
> +++ b/drivers/media/platform/verisilicon/hantro_hw.h
> @@ -162,6 +162,7 @@ struct hantro_hevc_dec_hw_ctx {
> struct hantro_aux_buf scaling_lists;
> s32 ref_bufs_poc[NUM_REF_PICTURES];
> u32 ref_bufs_used;
> + struct vb2_v4l2_buffer *ref_vb2[NUM_REF_PICTURES];
> struct hantro_hevc_dec_ctrls ctrls;
> unsigned int num_tile_cols_allocated;
> bool use_compression;
> @@ -457,7 +458,10 @@ int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx);
> int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx);
> void hantro_hevc_ref_init(struct hantro_ctx *ctx);
> dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, s32 poc);
> -int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr);
> +int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc,
> + dma_addr_t addr,
> + struct vb2_v4l2_buffer *vb2);
> +int hantro_hevc_get_ref_buf_index(struct hantro_ctx *ctx, s32 poc);
>
> int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx);
> void rockchip_vpu981_av1_dec_exit(struct hantro_ctx *ctx);
> @@ -469,100 +473,7 @@ static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension)
> return (dimension + 63) / 64;
> }
>
> -static inline size_t
> -hantro_vp9_mv_size(unsigned int width, unsigned int height)
> -{
> - int num_ctbs;
> -
> - /*
> - * There can be up to (CTBs x 64) number of blocks,
> - * and the motion vector for each block needs 16 bytes.
> - */
> - num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height);
> - return (num_ctbs * 64) * 16;
> -}
> -
> -static inline size_t
> -hantro_h264_mv_size(unsigned int width, unsigned int height)
> -{
> - /*
> - * A decoded 8-bit 4:2:0 NV12 frame may need memory for up to
> - * 448 bytes per macroblock with additional 32 bytes on
> - * multi-core variants.
> - *
> - * The H264 decoder needs extra space on the output buffers
> - * to store motion vectors. This is needed for reference
> - * frames and only if the format is non-post-processed NV12.
> - *
> - * Memory layout is as follow:
> - *
> - * +---------------------------+
> - * | Y-plane 256 bytes x MBs |
> - * +---------------------------+
> - * | UV-plane 128 bytes x MBs |
> - * +---------------------------+
> - * | MV buffer 64 bytes x MBs |
> - * +---------------------------+
> - * | MC sync 32 bytes |
> - * +---------------------------+
> - */
> - return 64 * MB_WIDTH(width) * MB_WIDTH(height) + 32;
> -}
> -
> -static inline size_t
> -hantro_hevc_mv_size(unsigned int width, unsigned int height)
> -{
> - /*
> - * A CTB can be 64x64, 32x32 or 16x16.
> - * Allocated memory for the "worse" case: 16x16
> - */
> - return width * height / 16;
> -}
> -
> -static inline size_t
> -hantro_hevc_luma_compressed_size(unsigned int width, unsigned int height)
> -{
> - u32 pic_width_in_cbsy =
> - round_up((width + CBS_LUMA - 1) / CBS_LUMA, CBS_SIZE);
> - u32 pic_height_in_cbsy = (height + CBS_LUMA - 1) / CBS_LUMA;
> -
> - return round_up(pic_width_in_cbsy * pic_height_in_cbsy, CBS_SIZE);
> -}
> -
> -static inline size_t
> -hantro_hevc_chroma_compressed_size(unsigned int width, unsigned int height)
> -{
> - u32 pic_width_in_cbsc =
> - round_up((width + CBS_CHROMA_W - 1) / CBS_CHROMA_W, CBS_SIZE);
> - u32 pic_height_in_cbsc = (height / 2 + CBS_CHROMA_H - 1) / CBS_CHROMA_H;
> -
> - return round_up(pic_width_in_cbsc * pic_height_in_cbsc, CBS_SIZE);
> -}
> -
> -static inline size_t
> -hantro_hevc_compressed_size(unsigned int width, unsigned int height)
> -{
> - return hantro_hevc_luma_compressed_size(width, height) +
> - hantro_hevc_chroma_compressed_size(width, height);
> -}
> -
> -static inline unsigned short hantro_av1_num_sbs(unsigned short dimension)
> -{
> - return DIV_ROUND_UP(dimension, 64);
> -}
> -
> -static inline size_t
> -hantro_av1_mv_size(unsigned int width, unsigned int height)
> -{
> - size_t num_sbs = hantro_av1_num_sbs(width) * hantro_av1_num_sbs(height);
> -
> - return ALIGN(num_sbs * 384, 16) * 2 + 512;
> -}
> -
> size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx);
> -size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx);
> -size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx);
> -size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx);
>
> int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx);
> int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx);
> diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c
> index e94d1ba5ef10..2409353c16e4 100644
> --- a/drivers/media/platform/verisilicon/hantro_postproc.c
> +++ b/drivers/media/platform/verisilicon/hantro_postproc.c
> @@ -196,36 +196,11 @@ void hantro_postproc_free(struct hantro_ctx *ctx)
> }
> }
>
> -static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx)
> -{
> - unsigned int buf_size;
> -
> - buf_size = ctx->ref_fmt.plane_fmt[0].sizeimage;
> - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE)
> - buf_size += hantro_h264_mv_size(ctx->ref_fmt.width,
> - ctx->ref_fmt.height);
> - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME)
> - buf_size += hantro_vp9_mv_size(ctx->ref_fmt.width,
> - ctx->ref_fmt.height);
> - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) {
> - buf_size += hantro_hevc_mv_size(ctx->ref_fmt.width,
> - ctx->ref_fmt.height);
> - if (ctx->hevc_dec.use_compression)
> - buf_size += hantro_hevc_compressed_size(ctx->ref_fmt.width,
> - ctx->ref_fmt.height);
> - }
> - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_AV1_FRAME)
> - buf_size += hantro_av1_mv_size(ctx->ref_fmt.width,
> - ctx->ref_fmt.height);
> -
> - return buf_size;
> -}
> -
> static int hantro_postproc_alloc(struct hantro_ctx *ctx, int index)
> {
> struct hantro_dev *vpu = ctx->dev;
> struct hantro_aux_buf *priv = &ctx->postproc.dec_q[index];
> - unsigned int buf_size = hantro_postproc_buffer_size(ctx);
> + unsigned int buf_size = ctx->ref_fmt.plane_fmt[0].sizeimage;
>
> if (!buf_size)
> return -EINVAL;
> @@ -267,7 +242,7 @@ dma_addr_t
> hantro_postproc_get_dec_buf_addr(struct hantro_ctx *ctx, int index)
> {
> struct hantro_aux_buf *priv = &ctx->postproc.dec_q[index];
> - unsigned int buf_size = hantro_postproc_buffer_size(ctx);
> + unsigned int buf_size = ctx->ref_fmt.plane_fmt[0].sizeimage;
> struct hantro_dev *vpu = ctx->dev;
> int ret;
>
> diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
> index fcf3bd9bcda2..f8d4dd518368 100644
> --- a/drivers/media/platform/verisilicon/hantro_v4l2.c
> +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
> @@ -36,6 +36,9 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx,
> static int hantro_set_fmt_cap(struct hantro_ctx *ctx,
> struct v4l2_pix_format_mplane *pix_mp);
>
> +static void hantro_mv_free(struct hantro_ctx *ctx);
> +static void hantro_rfc_free(struct hantro_ctx *ctx);
> +
> static const struct hantro_fmt *
> hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts, bool need_postproc)
> {
> @@ -362,26 +365,6 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx,
> /* Fill remaining fields */
> v4l2_fill_pixfmt_mp(pix_mp, fmt->fourcc, pix_mp->width,
> pix_mp->height);
> - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE &&
> - !hantro_needs_postproc(ctx, fmt))
> - pix_mp->plane_fmt[0].sizeimage +=
> - hantro_h264_mv_size(pix_mp->width,
> - pix_mp->height);
> - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME &&
> - !hantro_needs_postproc(ctx, fmt))
> - pix_mp->plane_fmt[0].sizeimage +=
> - hantro_vp9_mv_size(pix_mp->width,
> - pix_mp->height);
> - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE &&
> - !hantro_needs_postproc(ctx, fmt))
> - pix_mp->plane_fmt[0].sizeimage +=
> - hantro_hevc_mv_size(pix_mp->width,
> - pix_mp->height);
> - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_AV1_FRAME &&
> - !hantro_needs_postproc(ctx, fmt))
> - pix_mp->plane_fmt[0].sizeimage +=
> - hantro_av1_mv_size(pix_mp->width,
> - pix_mp->height);
> } else if (!pix_mp->plane_fmt[0].sizeimage) {
> /*
> * For coded formats the application can specify
> @@ -984,6 +967,9 @@ static void hantro_stop_streaming(struct vb2_queue *q)
> ctx->codec_ops->exit(ctx);
> }
>
> + hantro_mv_free(ctx);
> + hantro_rfc_free(ctx);
> +
> /*
> * The mem2mem framework calls v4l2_m2m_cancel_job before
> * .stop_streaming, so there isn't any job running and
> @@ -1025,3 +1011,291 @@ const struct vb2_ops hantro_queue_ops = {
> .start_streaming = hantro_start_streaming,
> .stop_streaming = hantro_stop_streaming,
> };
> +
> +static inline size_t
> +hantro_vp9_mv_size(unsigned int width, unsigned int height)
I don't like much that we are adding more codec specific function in
hantro_v4l2.c. Can we move these into codec specific headers (since this is
inline), just to keep things separate.
> +{
> + int num_ctbs;
> +
> + /*
> + * There can be up to (CTBs x 64) number of blocks,
> + * and the motion vector for each block needs 16 bytes.
> + */
> + num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height);
> + return (num_ctbs * 64) * 16;
> +}
> +
> +static inline size_t
> +hantro_h264_mv_size(unsigned int width, unsigned int height)
> +{
> + /*
> + * A decoded 8-bit 4:2:0 NV12 frame may need memory for up to
> + * 448 bytes per macroblock with additional 32 bytes on
> + * multi-core variants.
> + *
> + * The H264 decoder needs extra space on the output buffers
> + * to store motion vectors. This is needed for reference
> + * frames and only if the format is non-post-processed NV12.
> + *
> + * Memory layout is as follow:
> + *
> + * +---------------------------+
> + * | Y-plane 256 bytes x MBs |
> + * +---------------------------+
> + * | UV-plane 128 bytes x MBs |
> + * +---------------------------+
> + * | MV buffer 64 bytes x MBs |
> + * +---------------------------+
> + * | MC sync 32 bytes |
> + * +---------------------------+
> + */
> + return 64 * MB_WIDTH(width) * MB_WIDTH(height) + 32;
> +}
> +
> +static inline size_t
> +hantro_hevc_mv_size(unsigned int width, unsigned int height, int depth)
> +{
> + /*
> + * A CTB can be 64x64, 32x32 or 16x16.
> + * Allocated memory for the "worse" case: 16x16
> + */
> + return DIV_ROUND_UP(width * height * depth / 8, 128);
> +}
> +
> +static inline unsigned short hantro_av1_num_sbs(unsigned short dimension)
> +{
> + return DIV_ROUND_UP(dimension, 64);
> +}
> +
> +static inline size_t
> +hantro_av1_mv_size(unsigned int width, unsigned int height)
> +{
> + size_t num_sbs = hantro_av1_num_sbs(width) * hantro_av1_num_sbs(height);
> +
> + return ALIGN(num_sbs * 384, 16) * 2 + 512;
> +}
> +
> +static void hantro_mv_free(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + int i;
> +
> + for (i = 0; i < MAX_MV_BUFFERS; i++) {
> + struct hantro_aux_buf *mv = &ctx->dec_mv[i];
> +
> + if (!mv->cpu)
> + continue;
> +
> + dma_free_attrs(vpu->dev, mv->size, mv->cpu,
> + mv->dma, mv->attrs);
> + mv->cpu = NULL;
> + }
> +}
> +
> +static unsigned int hantro_mv_buffer_size(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + int fourcc = ctx->vpu_src_fmt->fourcc;
> + int width = ctx->ref_fmt.width;
> + int height = ctx->ref_fmt.height;
> +
> + switch (fourcc) {
> + case V4L2_PIX_FMT_H264_SLICE:
> + return hantro_h264_mv_size(width, height);
> + case V4L2_PIX_FMT_VP9_FRAME:
> + return hantro_vp9_mv_size(width, height);
> + case V4L2_PIX_FMT_HEVC_SLICE:
> + return hantro_hevc_mv_size(width, height, ctx->bit_depth);
> + case V4L2_PIX_FMT_AV1_FRAME:
> + return hantro_av1_mv_size(width, height);
> + }
> +
> + /* Should not happen */
> + dev_warn(vpu->dev, "Invalid motion vectors size\n");
> + return 0;
> +}
> +
> +static int hantro_mv_buffer_alloc(struct hantro_ctx *ctx, int index)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + struct hantro_aux_buf *mv = &ctx->dec_mv[index];
> + unsigned int buf_size = hantro_mv_buffer_size(ctx);
> +
> + if (!buf_size)
> + return -EINVAL;
> +
> + /*
> + * Motion vectors buffers are only read and write by the
> + * hardware so no mapping is needed.
> + */
> + mv->attrs = DMA_ATTR_NO_KERNEL_MAPPING;
> + mv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &mv->dma,
> + GFP_KERNEL, mv->attrs);
> + if (!mv->cpu)
> + return -ENOMEM;
> + mv->size = buf_size;
> +
> + return 0;
> +}
> +
> +dma_addr_t
> +hantro_mv_get_buf_addr(struct hantro_ctx *ctx, int index)
> +{
> + struct hantro_aux_buf *mv = &ctx->dec_mv[index];
> + unsigned int buf_size = hantro_mv_buffer_size(ctx);
> + struct hantro_dev *vpu = ctx->dev;
> + int ret;
> +
> + if (mv->size < buf_size && mv->cpu) {
> + /* buffer is too small, release it */
> + dma_free_attrs(vpu->dev, mv->size, mv->cpu,
> + mv->dma, mv->attrs);
> + mv->cpu = NULL;
> + }
> +
> + if (!mv->cpu) {
> + /* buffer not already allocated, try getting a new one */
> + ret = hantro_mv_buffer_alloc(ctx, index);
> + if (ret)
> + return 0;
> + }
> +
> + if (!mv->cpu)
> + return 0;
> +
> + return mv->dma;
> +}
> +
> +static inline size_t
> +hantro_hevc_luma_compressed_size(unsigned int width, unsigned int height)
> +{
> + u32 pic_width_in_cbsy =
> + round_up((width + CBS_LUMA - 1) / CBS_LUMA, CBS_SIZE);
> + u32 pic_height_in_cbsy = (height + CBS_LUMA - 1) / CBS_LUMA;
> +
> + return round_up(pic_width_in_cbsy * pic_height_in_cbsy, CBS_SIZE);
> +}
> +
> +static inline size_t
> +hantro_hevc_chroma_compressed_size(unsigned int width, unsigned int height)
> +{
> + u32 pic_width_in_cbsc =
> + round_up((width + CBS_CHROMA_W - 1) / CBS_CHROMA_W, CBS_SIZE);
> + u32 pic_height_in_cbsc = (height / 2 + CBS_CHROMA_H - 1) / CBS_CHROMA_H;
> +
> + return round_up(pic_width_in_cbsc * pic_height_in_cbsc, CBS_SIZE);
> +}
> +
> +static inline size_t
> +hantro_hevc_compressed_size(unsigned int width, unsigned int height)
> +{
> + return hantro_hevc_luma_compressed_size(width, height) +
> + hantro_hevc_chroma_compressed_size(width, height);
> +}
> +
> +static void hantro_rfc_free(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + int i;
> +
> + for (i = 0; i < MAX_MV_BUFFERS; i++) {
> + struct hantro_aux_buf *rfc = &ctx->dec_rfc[i];
> +
> + if (!rfc->cpu)
> + continue;
> +
> + dma_free_attrs(vpu->dev, rfc->size, rfc->cpu,
> + rfc->dma, rfc->attrs);
> + rfc->cpu = NULL;
> + }
> +}
> +
> +static unsigned int hantro_rfc_buffer_size(struct hantro_ctx *ctx)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + int fourcc = ctx->vpu_src_fmt->fourcc;
> + int width = ctx->ref_fmt.width;
> + int height = ctx->ref_fmt.height;
> +
> + switch (fourcc) {
> + case V4L2_PIX_FMT_HEVC_SLICE:
> + return hantro_hevc_compressed_size(width, height);
> + }
> +
> + /* Should not happen */
> + dev_warn(vpu->dev, "Invalid rfc size\n");
> + return 0;
> +}
> +
> +static int hantro_rfc_buffer_alloc(struct hantro_ctx *ctx, int index)
> +{
> + struct hantro_dev *vpu = ctx->dev;
> + struct hantro_aux_buf *rfc = &ctx->dec_rfc[index];
> + unsigned int buf_size = hantro_rfc_buffer_size(ctx);
> +
> + if (!buf_size)
> + return -EINVAL;
> +
> + /*
> + * RFC buffers are only read and write by the
> + * hardware so no mapping is needed.
> + */
> + rfc->attrs = DMA_ATTR_NO_KERNEL_MAPPING;
> + rfc->cpu = dma_alloc_attrs(vpu->dev, buf_size, &rfc->dma,
> + GFP_KERNEL, rfc->attrs);
> + if (!rfc->cpu)
> + return -ENOMEM;
> + rfc->size = buf_size;
> +
> + return 0;
> +}
> +
> +dma_addr_t
> +hantro_rfc_get_luma_buf_addr(struct hantro_ctx *ctx, int index)
> +{
> + struct hantro_aux_buf *rfc = &ctx->dec_rfc[index];
> + unsigned int buf_size = hantro_rfc_buffer_size(ctx);
> + struct hantro_dev *vpu = ctx->dev;
> + int ret;
> +
> + if (rfc->size < buf_size && rfc->cpu) {
> + /* buffer is too small, release it */
> + dma_free_attrs(vpu->dev, rfc->size, rfc->cpu,
> + rfc->dma, rfc->attrs);
> + rfc->cpu = NULL;
> + }
> +
> + if (!rfc->cpu) {
> + /* buffer not already allocated, try getting a new one */
> + ret = hantro_rfc_buffer_alloc(ctx, index);
> + if (ret)
> + return 0;
> + }
> +
> + if (!rfc->cpu)
> + return 0;
> +
> + return rfc->dma;
> +}
> +
> +dma_addr_t
> +hantro_rfc_get_chroma_buf_addr(struct hantro_ctx *ctx, int index)
> +{
> + dma_addr_t luma_addr = hantro_rfc_get_luma_buf_addr(ctx, index);
> + struct hantro_dev *vpu = ctx->dev;
> + int fourcc = ctx->vpu_src_fmt->fourcc;
> + int width = ctx->ref_fmt.width;
> + int height = ctx->ref_fmt.height;
> +
> + if (!luma_addr)
> + return -EINVAL;
> +
> + switch (fourcc) {
> + case V4L2_PIX_FMT_HEVC_SLICE:
> + return luma_addr + hantro_hevc_luma_compressed_size(width, height);
> + }
> +
> + /* Should not happen */
> + dev_warn(vpu->dev, "Invalid rfc chroma address\n");
> + return 0;
> +}
> diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> index c1ada14df4c3..21da8ddfc4b3 100644
> --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> @@ -62,7 +62,7 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx,
> const struct v4l2_ctrl_av1_frame *frame = ctrls->frame;
> struct hantro_dev *vpu = ctx->dev;
> struct hantro_decoded_buffer *dst;
> - dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
> + dma_addr_t luma_addr, chroma_addr = 0;
> int cur_width = frame->frame_width_minus_1 + 1;
> int cur_height = frame->frame_height_minus_1 + 1;
> int scale_width =
> @@ -120,11 +120,10 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx,
> dst = vb2_to_hantro_decoded_buf(&av1_dec->frame_refs[idx].vb2_ref->vb2_buf);
> luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf);
> chroma_addr = luma_addr + dst->av1.chroma_offset;
> - mv_addr = luma_addr + dst->av1.mv_offset;
>
> hantro_write_addr(vpu, AV1_REFERENCE_Y(ref), luma_addr);
> hantro_write_addr(vpu, AV1_REFERENCE_CB(ref), chroma_addr);
> - hantro_write_addr(vpu, AV1_REFERENCE_MV(ref), mv_addr);
> + hantro_write_addr(vpu, AV1_REFERENCE_MV(ref), dst->av1.mv_addr);
>
> return (scale_width != (1 << AV1_REF_SCALE_SHIFT)) ||
> (scale_height != (1 << AV1_REF_SCALE_SHIFT));
> @@ -180,11 +179,10 @@ static void rockchip_vpu981_av1_dec_set_segmentation(struct hantro_ctx *ctx)
> if (idx >= 0) {
> dma_addr_t luma_addr, mv_addr = 0;
> struct hantro_decoded_buffer *seg;
> - size_t mv_offset = hantro_av1_chroma_size(ctx);
>
> seg = vb2_to_hantro_decoded_buf(&av1_dec->frame_refs[idx].vb2_ref->vb2_buf);
> luma_addr = hantro_get_dec_buf_addr(ctx, &seg->base.vb.vb2_buf);
> - mv_addr = luma_addr + mv_offset;
> + mv_addr = hantro_mv_get_buf_addr(ctx, seg->base.vb.vb2_buf.index);
>
> hantro_write_addr(vpu, AV1_SEGMENTATION, mv_addr);
> hantro_reg_write(vpu, &av1_use_temporal3_mvs, 1);
> @@ -1350,22 +1348,20 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx)
> struct hantro_dev *vpu = ctx->dev;
> struct hantro_decoded_buffer *dst;
> struct vb2_v4l2_buffer *vb2_dst;
> - dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
> + dma_addr_t luma_addr, chroma_addr = 0;
> size_t cr_offset = hantro_av1_luma_size(ctx);
> - size_t mv_offset = hantro_av1_chroma_size(ctx);
>
> vb2_dst = av1_dec->frame_refs[av1_dec->current_frame_index].vb2_ref;
> dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
> luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf);
> chroma_addr = luma_addr + cr_offset;
> - mv_addr = luma_addr + mv_offset;
>
> dst->av1.chroma_offset = cr_offset;
> - dst->av1.mv_offset = mv_offset;
> + dst->av1.mv_addr = hantro_mv_get_buf_addr(ctx, dst->base.vb.vb2_buf.index);
>
> hantro_write_addr(vpu, AV1_TILE_OUT_LU, luma_addr);
> hantro_write_addr(vpu, AV1_TILE_OUT_CH, chroma_addr);
> - hantro_write_addr(vpu, AV1_TILE_OUT_MV, mv_addr);
> + hantro_write_addr(vpu, AV1_TILE_OUT_MV, dst->av1.mv_addr);
> }
>
> int rockchip_vpu981_av1_dec_run(struct hantro_ctx *ctx)
I like the direction this is going, as it removes a lot of stride/offset open
calculation, which has been source of problem, and it also reduce the memory
allocation overhead. My main worry is that we don't tighly manages the entries
based on the DPB (references). So even if a reference have gone away, we don't
explicitly reset the entry and prevent them from being used. I'd like to see
that improved.
Nicolas
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH 1/2] KVM: arm64: Add KVM_CAP_ARM_DISABLE_EXITS for WFI/WFE passthrough
From: David Woodhouse @ 2026-04-08 20:23 UTC (permalink / raw)
To: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Marc Zyngier,
Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, kvm, linux-doc, linux-kernel,
linux-arm-kernel, kvmarm, linux-kselftest, Colton Lewis,
Jing Zhang, David Woodhouse
In-Reply-To: <20260408202557.2102476-1-dwmw2@infradead.org>
From: David Woodhouse <dwmw@amazon.co.uk>
Add a per-VM capability to allow userspace to disable WFI and/or WFE
trapping, modelled after x86's KVM_CAP_X86_DISABLE_EXITS. When the
corresponding flag is set, the trap is unconditionally cleared
regardless of the global kvm-arm.wf{i,e}_trap_policy setting.
The existing kernel command line parameters provide a system-wide
override, but a per-VM capability allows the VMM to make the decision
per guest.
This is useful for hypervisors running a combination of dedicated
pinned vCPUs which want to avoid the cost of trapping WFI/WFE, as
well as overcommitted floating instances where it is necessary.
As with the x86 equivalent, KVM_CHECK_EXTENSION returns the bitmask of
supported exit disables.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
Documentation/virt/kvm/api.rst | 28 ++++++++++++++++++++++++++++
arch/arm64/include/asm/kvm_host.h | 4 ++++
arch/arm64/kvm/arm.c | 20 ++++++++++++++++++++
include/uapi/linux/kvm.h | 6 ++++++
4 files changed, 58 insertions(+)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 032516783e96..e3b3bd9edeec 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -8902,6 +8902,34 @@ helpful if user space wants to emulate instructions which are not
This capability can be enabled dynamically even if VCPUs were already
created and are running.
+7.47 KVM_CAP_ARM_DISABLE_EXITS
+------------------------------
+
+:Architecture: arm64
+:Target: VM
+:Parameters: args[0] is a bitmask of exits to disable
+:Returns: 0 on success, -EINVAL if unsupported bits are set.
+
+Valid bits in args[0]:
+
+ - ``KVM_ARM_DISABLE_EXITS_WFI``: Disable trapping of WFI (Wait For
+ Interrupt) instructions. The guest WFI will execute natively instead
+ of causing a VM exit.
+
+ - ``KVM_ARM_DISABLE_EXITS_WFE``: Disable trapping of WFE (Wait For
+ Event) instructions. The guest WFE will execute natively instead of
+ causing a VM exit.
+
+When a bit is set, the corresponding trap is unconditionally cleared for
+all vCPUs in the VM, overriding the system-wide ``kvm-arm.wfi_trap_policy``
+and ``kvm-arm.wfe_trap_policy`` kernel parameters.
+
+Disabling exits is a one-way operation: once an exit type is disabled for
+a VM, it cannot be re-enabled. Calling this ioctl with args[0] = 0 is a
+no-op.
+
+``KVM_CHECK_EXTENSION`` returns the bitmask of exits that can be disabled.
+
8. Other capabilities.
======================
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 70cb9cfd760a..a1bb025c641f 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -312,6 +312,10 @@ struct kvm_arch {
size_t nested_mmus_size;
int nested_mmus_next;
+ /* Per-VM WFI trap override; set via KVM_CAP_ARM_DISABLE_EXITS */
+ bool wfi_in_guest;
+ bool wfe_in_guest;
+
/* Interrupt controller */
struct vgic_dist vgic;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 410ffd41fd73..326a99fea753 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -178,6 +178,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
}
mutex_unlock(&kvm->lock);
break;
+ case KVM_CAP_ARM_DISABLE_EXITS:
+ if (cap->args[0] & ~KVM_ARM_DISABLE_VALID_EXITS) {
+ r = -EINVAL;
+ break;
+ }
+ if (cap->args[0] & KVM_ARM_DISABLE_EXITS_WFI)
+ kvm->arch.wfi_in_guest = true;
+ if (cap->args[0] & KVM_ARM_DISABLE_EXITS_WFE)
+ kvm->arch.wfe_in_guest = true;
+ r = 0;
+ break;
case KVM_CAP_ARM_SEA_TO_USER:
r = 0;
set_bit(KVM_ARCH_FLAG_EXIT_SEA, &kvm->arch.flags);
@@ -379,6 +390,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_ARM_SEA_TO_USER:
r = 1;
break;
+ case KVM_CAP_ARM_DISABLE_EXITS:
+ r = KVM_ARM_DISABLE_VALID_EXITS;
+ break;
case KVM_CAP_SET_GUEST_DEBUG2:
return KVM_GUESTDBG_VALID_MASK;
case KVM_CAP_ARM_SET_DEVICE_ADDR:
@@ -610,6 +624,9 @@ static void vcpu_set_pauth_traps(struct kvm_vcpu *vcpu)
static bool kvm_vcpu_should_clear_twi(struct kvm_vcpu *vcpu)
{
+ if (vcpu->kvm->arch.wfi_in_guest)
+ return true;
+
if (unlikely(kvm_wfi_trap_policy != KVM_WFX_NOTRAP_SINGLE_TASK))
return kvm_wfi_trap_policy == KVM_WFX_NOTRAP;
@@ -621,6 +638,9 @@ static bool kvm_vcpu_should_clear_twi(struct kvm_vcpu *vcpu)
static bool kvm_vcpu_should_clear_twe(struct kvm_vcpu *vcpu)
{
+ if (vcpu->kvm->arch.wfe_in_guest)
+ return true;
+
if (unlikely(kvm_wfe_trap_policy != KVM_WFX_NOTRAP_SINGLE_TASK))
return kvm_wfe_trap_policy == KVM_WFX_NOTRAP;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 80364d4dbebb..694cf699ed0a 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -669,6 +669,11 @@ struct kvm_ioeventfd {
#define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3)
#define KVM_X86_DISABLE_EXITS_APERFMPERF (1 << 4)
+#define KVM_ARM_DISABLE_EXITS_WFI (1 << 0)
+#define KVM_ARM_DISABLE_EXITS_WFE (1 << 1)
+#define KVM_ARM_DISABLE_VALID_EXITS (KVM_ARM_DISABLE_EXITS_WFI | \
+ KVM_ARM_DISABLE_EXITS_WFE)
+
/* for KVM_ENABLE_CAP */
struct kvm_enable_cap {
/* in */
@@ -989,6 +994,7 @@ struct kvm_enable_cap {
#define KVM_CAP_ARM_SEA_TO_USER 245
#define KVM_CAP_S390_USER_OPEREXEC 246
#define KVM_CAP_S390_KEYOP 247
+#define KVM_CAP_ARM_DISABLE_EXITS 248
struct kvm_irq_routing_irqchip {
__u32 irqchip;
--
2.51.0
^ permalink raw reply related
* [PATCH 2/2] KVM: arm64: selftests: Add KVM_CAP_ARM_DISABLE_EXITS UAPI test
From: David Woodhouse @ 2026-04-08 20:23 UTC (permalink / raw)
To: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Marc Zyngier,
Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, kvm, linux-doc, linux-kernel,
linux-arm-kernel, kvmarm, linux-kselftest, Colton Lewis,
Jing Zhang, David Woodhouse
In-Reply-To: <20260408202557.2102476-1-dwmw2@infradead.org>
From: David Woodhouse <dwmw@amazon.co.uk>
Test the KVM_CAP_ARM_DISABLE_EXITS capability interface:
- KVM_CHECK_EXTENSION reports KVM_ARM_DISABLE_EXITS_WFI
- KVM_ENABLE_CAP succeeds with valid flags (WFI, zero)
- KVM_ENABLE_CAP fails with EINVAL for unknown flags
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/arm64/disable_exits.c | 48 +++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 tools/testing/selftests/kvm/arm64/disable_exits.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 878d7cb92555..d8e7ff122445 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -179,6 +179,7 @@ TEST_GEN_PROGS_arm64 += arm64/vgic_irq
TEST_GEN_PROGS_arm64 += arm64/vgic_lpi_stress
TEST_GEN_PROGS_arm64 += arm64/vgic_group_iidr
TEST_GEN_PROGS_arm64 += arm64/vgic_group_v2
+TEST_GEN_PROGS_arm64 += arm64/disable_exits
TEST_GEN_PROGS_arm64 += arm64/vpmu_counter_access
TEST_GEN_PROGS_arm64 += arm64/no-vgic-v3
TEST_GEN_PROGS_arm64 += arm64/idreg-idst
diff --git a/tools/testing/selftests/kvm/arm64/disable_exits.c b/tools/testing/selftests/kvm/arm64/disable_exits.c
new file mode 100644
index 000000000000..27fe6c9297b2
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/disable_exits.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * disable_exits.c - Test KVM_CAP_ARM_DISABLE_EXITS UAPI
+ *
+ * Verify that KVM_CHECK_EXTENSION reports the valid exit disable mask
+ * and that KVM_ENABLE_CAP accepts valid flags and rejects invalid ones.
+ */
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vm *vm;
+ int r;
+
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_DISABLE_EXITS));
+
+ r = kvm_check_cap(KVM_CAP_ARM_DISABLE_EXITS);
+ TEST_ASSERT(r & KVM_ARM_DISABLE_EXITS_WFI,
+ "KVM_CHECK_EXTENSION should report WFI: got 0x%x", r);
+ TEST_ASSERT(r & KVM_ARM_DISABLE_EXITS_WFE,
+ "KVM_CHECK_EXTENSION should report WFE: got 0x%x", r);
+
+ vm = vm_create(1);
+
+ /* Valid: disable WFI trapping */
+ vm_enable_cap(vm, KVM_CAP_ARM_DISABLE_EXITS, KVM_ARM_DISABLE_EXITS_WFI);
+
+ /* Valid: disable WFE trapping */
+ vm_enable_cap(vm, KVM_CAP_ARM_DISABLE_EXITS, KVM_ARM_DISABLE_EXITS_WFE);
+
+ /* Valid: disable both */
+ vm_enable_cap(vm, KVM_CAP_ARM_DISABLE_EXITS,
+ KVM_ARM_DISABLE_EXITS_WFI | KVM_ARM_DISABLE_EXITS_WFE);
+
+ /* Valid: no exits disabled (no-op) */
+ vm_enable_cap(vm, KVM_CAP_ARM_DISABLE_EXITS, 0);
+
+ /* Invalid: unknown bit set */
+ r = __vm_enable_cap(vm, KVM_CAP_ARM_DISABLE_EXITS, 1ULL << 31);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "Unknown flags should fail with EINVAL: got %d errno %d",
+ r, errno);
+
+ kvm_vm_free(vm);
+ return 0;
+}
--
2.51.0
^ permalink raw reply related
* [PATCH 0/2] KVM: arm64: KVM: arm64: Add per-VM WFI/WFE exit disable capability
From: David Woodhouse @ 2026-04-08 20:23 UTC (permalink / raw)
To: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Marc Zyngier,
Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, kvm, linux-doc, linux-kernel,
linux-arm-kernel, kvmarm, linux-kselftest, Colton Lewis,
Jing Zhang, David Woodhouse
Add KVM_CAP_ARM_DISABLE_EXITS, modelled after the existing x86
KVM_CAP_X86_DISABLE_EXITS, to allow userspace to disable WFI and/or
WFE trapping on a per-VM basis.
KVM already has system-wide kernel command line parameters
(kvm-arm.wfi_trap_policy and kvm-arm.wfe_trap_policy, added in
0b5afe05377d) to control WFx trapping. However, these are global and
set at boot time. A per-VM capability allows the VMM to make the
decision per guest — for example, disabling WFI trapping for
latency-sensitive VMs with pinned vCPUs while keeping it enabled for
overcommitted guests on the same host.
When a flag is set via KVM_ENABLE_CAP, the corresponding trap is
unconditionally cleared, overriding the system-wide policy. When the
flag is not set, the system policy (including the default
single-task heuristic) applies as before.
As with the x86 equivalent, disabling exits is a one-way operation
per VM.
Tested on Graviton 3 (Neoverse-V1) metal.
David Woodhouse (2):
KVM: arm64: Add KVM_CAP_ARM_DISABLE_EXITS for WFI/WFE passthrough
KVM: arm64: selftests: Add KVM_CAP_ARM_DISABLE_EXITS UAPI test
Documentation/virt/kvm/api.rst | 28 +++++++++++++
arch/arm64/include/asm/kvm_host.h | 4 ++
arch/arm64/kvm/arm.c | 20 ++++++++++
include/uapi/linux/kvm.h | 6 +++
tools/testing/selftests/kvm/Makefile.kvm | 1 +
tools/testing/selftests/kvm/arm64/disable_exits.c | 48 +++++++++++++++++++++++
6 files changed, 107 insertions(+)
create mode 100644 tools/testing/selftests/kvm/arm64/disable_exits.c
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox