All of lore.kernel.org
 help / color / mirror / Atom feed
From: Felipe Calliari <calliarifelipe@gmail.com>
To: linux-media@vger.kernel.org
Cc: Sakari Ailus <sakari.ailus@linux.intel.com>,
	Hans de Goede <hansg@kernel.org>,
	Bryan O'Donoghue <bod@kernel.org>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Dan Scally <dan.scally@ideasonboard.com>,
	Tomas Moro <tmorolias@gmail.com>,
	linux-kernel@vger.kernel.org,
	Felipe Calliari <calliarifelipe@gmail.com>
Subject: [PATCH v3 5/5] media: ov02c10: Add support for a 26 MHz external clock
Date: Wed, 23 Sep 2026 20:41:00 -0300	[thread overview]
Message-ID: <20260923234100.318151-6-calliarifelipe@gmail.com> (raw)
In-Reply-To: <20260923234100.318151-1-calliarifelipe@gmail.com>

Some Raptor Lake laptops, e.g. the Samsung Galaxy Book3 series, feed
the OV02C10 a 26 MHz external clock, which the driver rejects today.

Add a PLL configuration for a 26 MHz clock, with the OP and VT PLL
multipliers at 0x0127 instead of 0x0190. This yields a link frequency
of 399.479167 MHz, which is added as a second link frequency. Select
the PLL configuration and the link frequency, and thus the pixel rate,
based on the external clock rate.

While at it, terminate the unsupported-clock error message with a
newline.

Signed-off-by: Felipe Calliari <calliarifelipe@gmail.com>
---

Notes:
    Changes in v3:
    - Advertise 399.479167 MHz as a second link frequency instead of
      relying on it being close to 400 MHz. The pixel rate follows
      from it: 159.791666 MHz on two lanes (Sakari).
    - Choose between two PLL tables, split off in patch 3, from the
      external clock rate (Sakari). Fail probe if the firmware does
      not list the matching link frequency.
    - Make the last menu item the LINK_FREQ control's maximum and the
      selected frequency its default.
    - Stop writing 0x0315. Its reset value, 0x01, is what both tables
      need.
    - Drop the dev_dbg() and move the test details to the cover letter
      (Sakari).

 drivers/media/i2c/ov02c10.c | 75 +++++++++++++++++++++++++++++++++----
 1 file changed, 67 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c
index 0e5010303..c7a9f5da1 100644
--- a/drivers/media/i2c/ov02c10.c
+++ b/drivers/media/i2c/ov02c10.c
@@ -16,7 +16,9 @@
 #include <media/v4l2-fwnode.h>
 
 #define OV02C10_LINK_FREQ_400MHZ	400000000ULL
-#define OV02C10_MCLK			19200000
+#define OV02C10_LINK_FREQ_399MHZ	399479167ULL
+#define OV02C10_MCLK_19_2MHZ		19200000
+#define OV02C10_MCLK_26MHZ		26000000
 #define OV02C10_RGB_DEPTH		10
 
 #define OV02C10_NATIVE_WIDTH		1928
@@ -333,6 +335,22 @@ static const struct reg_sequence sensor_pll_19_2mhz_setting[] = {
 	{0x031c, 0x4f},
 };
 
+/*
+ * With the other PLL registers as for 19.2 MHz, the link frequency is
+ * mclk * M / 19.2 MHz, M being the OP (0x0304/0x0305) and VT (0x0315/0x0316)
+ * PLL multiplier.  400 MHz would need M = 295.38 at 26 MHz; M = 295 = 0x0127
+ * gives 399.479167 MHz.
+ */
+static const struct reg_sequence sensor_pll_26mhz_setting[] = {
+	{0x0301, 0x08},
+	{0x0303, 0x05},
+	{0x0304, 0x01},
+	{0x0305, 0x27},
+	{0x0313, 0x40},
+	{0x0316, 0x27},
+	{0x031c, 0x4f},
+};
+
 static const char * const ov02c10_test_pattern_menu[] = {
 	"Disabled",
 	"Color Bar",
@@ -343,6 +361,32 @@ static const char * const ov02c10_test_pattern_menu[] = {
 
 static const s64 link_freq_menu_items[] = {
 	OV02C10_LINK_FREQ_400MHZ,
+	OV02C10_LINK_FREQ_399MHZ,
+};
+
+struct ov02c10_clk_config {
+	/* External clock rate, Hz */
+	u32 mclk;
+	/* Index into link_freq_menu_items[] */
+	u32 link_freq_index;
+	/* PLL register settings for this clock rate */
+	const struct reg_sequence *pll_settings;
+	int pll_settings_length;
+};
+
+static const struct ov02c10_clk_config ov02c10_clk_configs[] = {
+	{
+		.mclk = OV02C10_MCLK_19_2MHZ,
+		.link_freq_index = 0,
+		.pll_settings = sensor_pll_19_2mhz_setting,
+		.pll_settings_length = ARRAY_SIZE(sensor_pll_19_2mhz_setting),
+	},
+	{
+		.mclk = OV02C10_MCLK_26MHZ,
+		.link_freq_index = 1,
+		.pll_settings = sensor_pll_26mhz_setting,
+		.pll_settings_length = ARRAY_SIZE(sensor_pll_26mhz_setting),
+	},
 };
 
 static const struct ov02c10_mode supported_modes[] = {
@@ -389,6 +433,8 @@ struct ov02c10 {
 	struct gpio_desc *reset;
 	struct regulator_bulk_data supplies[ARRAY_SIZE(ov02c10_supply_names)];
 
+	const struct ov02c10_clk_config *clk_config;
+
 	/* MIPI lane info */
 	u32 link_freq_index;
 	u8 mipi_lanes;
@@ -503,7 +549,8 @@ static int ov02c10_init_controls(struct ov02c10 *ov02c10)
 	ov02c10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
 						    &ov02c10_ctrl_ops,
 						    V4L2_CID_LINK_FREQ,
-						    ov02c10->link_freq_index, 0,
+						    ARRAY_SIZE(link_freq_menu_items) - 1,
+						    ov02c10->link_freq_index,
 						    link_freq_menu_items);
 	if (ov02c10->link_freq)
 		ov02c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -598,8 +645,8 @@ static int ov02c10_enable_streams(struct v4l2_subdev *sd,
 		return ret;
 
 	ret = regmap_multi_reg_write(ov02c10->regmap,
-				     sensor_pll_19_2mhz_setting,
-				     ARRAY_SIZE(sensor_pll_19_2mhz_setting));
+				     ov02c10->clk_config->pll_settings,
+				     ov02c10->clk_config->pll_settings_length);
 	if (ret) {
 		dev_err(ov02c10->dev, "failed to write PLL settings\n");
 		goto out;
@@ -878,8 +925,13 @@ static int ov02c10_check_hwcfg(struct ov02c10 *ov02c10)
 	if (ret)
 		goto check_hwcfg_error;
 
-	/* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */
-	ov02c10->link_freq_index = ffs(link_freq_bitmap) - 1;
+	ov02c10->link_freq_index = ov02c10->clk_config->link_freq_index;
+	if (!(link_freq_bitmap & BIT(ov02c10->link_freq_index))) {
+		ret = dev_err_probe(dev, -EINVAL,
+				    "link frequency %lld Hz is not supported\n",
+				    link_freq_menu_items[ov02c10->link_freq_index]);
+		goto check_hwcfg_error;
+	}
 
 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1 &&
 	    bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
@@ -916,6 +968,7 @@ static int ov02c10_probe(struct i2c_client *client)
 {
 	struct ov02c10 *ov02c10;
 	unsigned long freq;
+	unsigned int i;
 	int ret;
 
 	ov02c10 = devm_kzalloc(&client->dev, sizeof(*ov02c10), GFP_KERNEL);
@@ -930,9 +983,15 @@ static int ov02c10_probe(struct i2c_client *client)
 				     "failed to get imaging clock\n");
 
 	freq = clk_get_rate(ov02c10->img_clk);
-	if (freq != OV02C10_MCLK)
+	for (i = 0; i < ARRAY_SIZE(ov02c10_clk_configs); i++) {
+		if (freq == ov02c10_clk_configs[i].mclk) {
+			ov02c10->clk_config = &ov02c10_clk_configs[i];
+			break;
+		}
+	}
+	if (!ov02c10->clk_config)
 		return dev_err_probe(ov02c10->dev, -EINVAL,
-				     "external clock %lu is not supported",
+				     "external clock %lu is not supported\n",
 				     freq);
 
 	v4l2_i2c_subdev_init(&ov02c10->sd, client, &ov02c10_subdev_ops);
-- 
2.55.0


      parent reply	other threads:[~2026-09-23 23:42 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <Tms-fUHfw0sPf1-YX_rgSNq7z1MNyvtfCImHXhxAUQAoTq-fwKvZF-p8a3ozCxAJ7kJCo-lKEU_JSkfUJy6oIA==@protonmail.internalid>
2026-09-05  3:07 ` [PATCH 1/3] media: ov02c10: Drop duplicate register write Felipe Calliari
2026-09-05  3:07   ` [PATCH 2/3] media: ov02c10: Implement get_selection Felipe Calliari
2026-09-08  9:07     ` Bryan O'Donoghue
2026-09-05  3:07   ` [PATCH 3/3] media: ov02c10: Accept a 26 MHz external clock Felipe Calliari
2026-09-08  8:06     ` Sakari Ailus
2026-09-09  2:55       ` Felipe Calliari
2026-09-08  9:21     ` Bryan O'Donoghue
2026-09-22  6:35     ` Tomas Moro
2026-09-08  9:04   ` [PATCH 1/3] media: ov02c10: Drop duplicate register write Bryan O'Donoghue
2026-09-23 14:42   ` [PATCH v2 0/3] media: ov02c10: get_selection and 26 MHz clock support Felipe Calliari
2026-09-23 14:42     ` [PATCH v2 1/3] media: ov02c10: Drop duplicate register write Felipe Calliari
2026-09-23 14:42     ` [PATCH v2 2/3] media: ov02c10: Implement get_selection Felipe Calliari
2026-09-23 14:42     ` [PATCH v2 3/3] media: ov02c10: Accept a 26 MHz external clock Felipe Calliari
2026-09-23 20:54       ` Sakari Ailus
2026-09-23 23:40     ` [PATCH v3 0/5] media: ov02c10: get_selection and 26 MHz clock support Felipe Calliari
2026-09-23 23:40       ` [PATCH v3 1/5] media: ov02c10: Drop duplicate register write Felipe Calliari
2026-09-23 23:40       ` [PATCH v3 2/5] media: ov02c10: Implement get_selection Felipe Calliari
2026-09-23 23:40       ` [PATCH v3 3/5] media: ov02c10: Split the PLL registers off the mode tables Felipe Calliari
2026-10-02 20:46         ` Sakari Ailus
2026-09-23 23:40       ` [PATCH v3 4/5] media: ipu-bridge: Add the OV02C10 26 MHz link frequency Felipe Calliari
2026-09-23 23:41       ` Felipe Calliari [this message]

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=20260923234100.318151-6-calliarifelipe@gmail.com \
    --to=calliarifelipe@gmail.com \
    --cc=bod@kernel.org \
    --cc=dan.scally@ideasonboard.com \
    --cc=hansg@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    --cc=tmorolias@gmail.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.