From: Robert Mast <rn.mast@zonnet.nl>
To: hdegoede@redhat.com
Cc: mchehab@kernel.org, sakari.ailus@linux.intel.com,
linux-media@vger.kernel.org, linux-staging@lists.linux.dev,
andy@kernel.org, gregkh@linuxfoundation.org,
linux-kernel@vger.kernel.org, laurent.pinchart@ideasonboard.com,
Robert Mast <rmast@live.nl>
Subject: [PATCH 1/1] media: atomisp: mt9m114: Graceful teardown atomisp and mt9m114
Date: Sat, 18 Apr 2026 11:26:51 +0200 [thread overview]
Message-ID: <20260418092651.7873-2-rn.mast@zonnet.nl> (raw)
In-Reply-To: <20260418092651.7873-1-rn.mast@zonnet.nl>
From: Robert Mast <rmast@live.nl>
Signed-off-by: Robert Mast <rmast@live.nl>
---
drivers/media/i2c/mt9m114.c | 118 ++++++++++++++++--
.../media/atomisp/pci/atomisp_csi2_bridge.c | 1 +
.../staging/media/atomisp/pci/atomisp_v4l2.c | 3 +
3 files changed, 110 insertions(+), 12 deletions(-)
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
index 16b0ace15813..27432a9c683d 100644
--- a/drivers/media/i2c/mt9m114.c
+++ b/drivers/media/i2c/mt9m114.c
@@ -10,6 +10,8 @@
*/
#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
@@ -416,6 +418,7 @@ struct mt9m114 {
unsigned int frame_rate;
struct v4l2_ctrl *tpg[4];
+ struct completion unregistered;
} ifp;
};
@@ -1467,6 +1470,8 @@ static int mt9m114_pa_init(struct mt9m114 *sensor)
sd->ctrl_handler = hdl;
+ init_completion(&sensor->ifp.unregistered);
+
return 0;
error:
@@ -2056,8 +2061,13 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd,
static void mt9m114_ifp_unregistered(struct v4l2_subdev *sd)
{
struct mt9m114 *sensor = ifp_to_mt9m114(sd);
+ struct device *dev = &sensor->client->dev;
+
+ dev_dbg(dev, "ifp unregistered callback (ifp.v4l2_dev=%p, pa.v4l2_dev=%p)\n",
+ sensor->ifp.sd.v4l2_dev, sensor->pa.sd.v4l2_dev);
v4l2_device_unregister_subdev(&sensor->pa.sd);
+ complete(&sensor->ifp.unregistered);
}
static int mt9m114_ifp_registered(struct v4l2_subdev *sd)
@@ -2149,12 +2159,15 @@ static int mt9m114_ifp_init(struct mt9m114 *sensor)
V4L2_EXPOSURE_MANUAL, 0,
V4L2_EXPOSURE_AUTO);
- link_freq = v4l2_ctrl_new_int_menu(hdl, &mt9m114_ifp_ctrl_ops,
- V4L2_CID_LINK_FREQ,
- sensor->bus_cfg.nr_of_link_frequencies - 1,
- 0, sensor->bus_cfg.link_frequencies);
- if (link_freq)
- link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (sensor->bus_cfg.nr_of_link_frequencies) {
+ link_freq = v4l2_ctrl_new_int_menu(hdl, &mt9m114_ifp_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ sensor->bus_cfg.nr_of_link_frequencies - 1,
+ 0,
+ sensor->bus_cfg.link_frequencies);
+ if (link_freq)
+ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ }
v4l2_ctrl_new_std(hdl, &mt9m114_ifp_ctrl_ops,
V4L2_CID_PIXEL_RATE,
@@ -2339,14 +2352,19 @@ static const struct dev_pm_ops mt9m114_pm_ops = {
static int mt9m114_verify_link_frequency(struct mt9m114 *sensor,
unsigned int pixrate)
{
+ u32 i;
unsigned int link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY
? pixrate * 8 : pixrate * 2;
- if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
- sensor->bus_cfg.link_frequencies[0] != link_freq)
+ if (!sensor->bus_cfg.nr_of_link_frequencies)
return -EINVAL;
- return 0;
+ for (i = 0; i < sensor->bus_cfg.nr_of_link_frequencies; i++) {
+ if (sensor->bus_cfg.link_frequencies[i] == link_freq)
+ return 0;
+ }
+
+ return -EINVAL;
}
/*
@@ -2383,6 +2401,29 @@ static int mt9m114_clk_init(struct mt9m114 *sensor)
unsigned int pixrate;
int ret;
+ if (!sensor->bus_cfg.nr_of_link_frequencies) {
+ /*
+ * ACPI fallback path: no reliable endpoint link frequency available.
+ * Use the default PLL target instead of EXTCLK bypass to avoid
+ * under-clocking the sensor and getting blank/timeout streams.
+ */
+ sensor->pll.ext_clock = clk_get_rate(sensor->clk);
+ sensor->pll.pix_clock = MT9M114_DEF_PIXCLOCK;
+
+ ret = aptina_pll_calculate(&sensor->client->dev, &limits,
+ &sensor->pll);
+ if (ret)
+ return ret;
+
+ sensor->pixrate = sensor->pll.ext_clock * sensor->pll.m
+ / (sensor->pll.n * sensor->pll.p1);
+ sensor->bypass_pll = false;
+
+ dev_warn(&sensor->client->dev,
+ "no link-frequencies provided, using default PLL clocking\n");
+ return 0;
+ }
+
/*
* Calculate the pixel rate and link frequency. The CSI-2 bus is clocked
* for 16-bit per pixel, transmitted in DDR over a single lane. For
@@ -2456,10 +2497,25 @@ static int mt9m114_identify(struct mt9m114 *sensor)
static int mt9m114_parse_dt(struct mt9m114 *sensor)
{
- struct fwnode_handle *fwnode = dev_fwnode(&sensor->client->dev);
+ struct fwnode_handle *fwnode;
struct fwnode_handle *ep;
int ret;
+#if IS_ENABLED(CONFIG_ACPI)
+ if (has_acpi_companion(&sensor->client->dev)) {
+ /*
+ * On some reload sequences a stale software-node graph can be
+ * observed for this ACPI-enumerated sensor. Use the known safe
+ * default bus configuration and skip endpoint graph parsing.
+ */
+ memset(&sensor->bus_cfg, 0, sizeof(sensor->bus_cfg));
+ sensor->bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY;
+ sensor->bus_cfg.bus.mipi_csi2.num_data_lanes = 1;
+ goto read_slew_rate;
+ }
+#endif
+ fwnode = dev_fwnode(&sensor->client->dev);
+
/*
* On ACPI systems the fwnode graph can be initialized by a bridge
* driver, which may not have probed yet. Wait for this.
@@ -2468,6 +2524,9 @@ static int mt9m114_parse_dt(struct mt9m114 *sensor)
* to the ACPI core.
*/
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (IS_ERR(ep))
+ return dev_err_probe(&sensor->client->dev, PTR_ERR(ep),
+ "failed to get fwnode graph endpoint\n");
if (!ep)
return dev_err_probe(&sensor->client->dev, -EPROBE_DEFER,
"waiting for fwnode graph endpoint\n");
@@ -2492,6 +2551,7 @@ static int mt9m114_parse_dt(struct mt9m114 *sensor)
goto error;
}
+read_slew_rate:
sensor->pad_slew_rate = MT9M114_PAD_SLEW_DEFAULT;
device_property_read_u32(&sensor->client->dev, "slew-rate",
&sensor->pad_slew_rate);
@@ -2629,8 +2689,22 @@ static void mt9m114_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct mt9m114 *sensor = ifp_to_mt9m114(sd);
struct device *dev = &client->dev;
-
- v4l2_async_unregister_subdev(&sensor->ifp.sd);
+ bool ifp_async_registered = sensor->ifp.sd.async_list.next;
+ bool ifp_bound = sensor->ifp.sd.v4l2_dev;
+
+ dev_dbg(dev,
+ "remove start (ifp_bound=%u ifp_async_registered=%u ifp.v4l2_dev=%p pa.v4l2_dev=%p)\n",
+ ifp_bound, ifp_async_registered,
+ sensor->ifp.sd.v4l2_dev, sensor->pa.sd.v4l2_dev);
+
+ if (ifp_async_registered) {
+ reinit_completion(&sensor->ifp.unregistered);
+ v4l2_async_unregister_subdev(&sensor->ifp.sd);
+ if (ifp_bound)
+ wait_for_completion(&sensor->ifp.unregistered);
+ } else {
+ dev_warn(dev, "ifp async subdev already unregistered, skipping\n");
+ }
mt9m114_ifp_cleanup(sensor);
mt9m114_pa_cleanup(sensor);
@@ -2646,6 +2720,25 @@ static void mt9m114_remove(struct i2c_client *client)
pm_runtime_set_suspended(dev);
}
+static void mt9m114_shutdown(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct mt9m114 *sensor;
+
+ if (!sd)
+ return;
+
+ sensor = ifp_to_mt9m114(sd);
+
+ if (sensor->streaming)
+ mt9m114_stop_streaming(sensor);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ mt9m114_power_off(sensor);
+ pm_runtime_set_suspended(&client->dev);
+}
+
static const struct of_device_id mt9m114_of_ids[] = {
{ .compatible = "onnn,mt9m114" },
{ /* sentinel */ },
@@ -2667,6 +2760,7 @@ static struct i2c_driver mt9m114_driver = {
},
.probe = mt9m114_probe,
.remove = mt9m114_remove,
+ .shutdown = mt9m114_shutdown,
};
module_i2c_driver(mt9m114_driver);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index 2a90f86e515f..87f8ddcd6651 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -547,6 +547,7 @@ int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp)
err_parse:
fwnode_handle_put(ep);
+ v4l2_async_nf_cleanup(&isp->notifier);
return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 900a67552d6a..32a1f85ab598 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -805,6 +805,9 @@ static void atomisp_unregister_entities(struct atomisp_device *isp)
unsigned int i;
struct v4l2_subdev *sd, *next;
+ v4l2_async_nf_unregister(&isp->notifier);
+ v4l2_async_nf_cleanup(&isp->notifier);
+
atomisp_subdev_unregister_entities(&isp->asd);
for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++)
atomisp_mipi_csi2_unregister_entities(&isp->csi2_port[i]);
--
2.53.0
next prev parent reply other threads:[~2026-04-18 10:27 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-18 9:26 [PATCH 0/1] media: atomisp: mt9m114: Graceful teardown atomisp and mt9m114 Robert Mast
2026-04-18 9:26 ` Robert Mast [this message]
2026-04-18 9:46 ` [PATCH 1/1] " Sakari Ailus
2026-04-22 12:09 ` kernel test robot
2026-04-22 14:37 ` kernel test robot
2026-04-18 11:10 ` [PATCH 0/1] " Laurent Pinchart
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260418092651.7873-2-rn.mast@zonnet.nl \
--to=rn.mast@zonnet.nl \
--cc=andy@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=hdegoede@redhat.com \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-staging@lists.linux.dev \
--cc=mchehab@kernel.org \
--cc=rmast@live.nl \
--cc=sakari.ailus@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox