public inbox for linux-staging@lists.linux.dev
 help / color / mirror / Atom feed
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 <rn.mast@zonnet.nl>
Subject: [PATCH v2 1/1] media: atomisp: mt9m114: Graceful teardown atomisp and mt9m114
Date: Sat, 18 Apr 2026 12:31:09 +0200	[thread overview]
Message-ID: <20260418103136.6911-2-rn.mast@zonnet.nl> (raw)
In-Reply-To: <20260418103136.6911-1-rn.mast@zonnet.nl>

Signed-off-by: Robert Mast <rn.mast@zonnet.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


  reply	other threads:[~2026-04-18 10:32 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-18 10:31 [PATCH 0/1] media: atomisp: mt9m114: Graceful teardown atomisp Robert Mast
2026-04-18 10:31 ` Robert Mast [this message]
2026-04-19  5:37   ` [PATCH v2 1/1] media: atomisp: mt9m114: Graceful teardown atomisp and mt9m114 Greg KH

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=20260418103136.6911-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=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