mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Gaurav Suman via B4 Relay <devnull+gauravsuman007.gmail.com@kernel.org>
To: Bryan O'Donoghue <bod@kernel.org>,
	 Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>,
	 Loic Poulain <loic.poulain@oss.qualcomm.com>,
	 Mauro Carvalho Chehab <mchehab@kernel.org>,
	Rob Herring <robh@kernel.org>,
	 Krzysztof Kozlowski <krzk+dt@kernel.org>,
	 Conor Dooley <conor+dt@kernel.org>,
	Gaurav Suman <gauravsuman007@gmail.com>,
	 Bryan O'Donoghue <bryan.odonoghue@linaro.org>,
	 Sakari Ailus <sakari.ailus@linux.intel.com>
Cc: linux-arm-msm@vger.kernel.org, linux-media@vger.kernel.org,
	 devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 8/8] media: i2c: Add ON Semiconductor LC898123F40 lens autofocus driver
Date: Mon, 09 Nov 2026 22:38:26 +0000	[thread overview]
Message-ID: <20261109-sm8150-camera-v1-8-3d2e7b407516@gmail.com> (raw)
In-Reply-To: <20261109-sm8150-camera-v1-0-3d2e7b407516@gmail.com>

From: Gaurav Suman <gauravsuman007@gmail.com>

Add a V4L2 lens sub-device driver for the autofocus function of the
LC898123F40 OIS/AF controller. The controller runs firmware from its
own flash; the lens position is a 32-bit value written to register
0xf01a, and 0xf012 = 1 enables the servo, as in the LG G8x ThinQ
vendor camera data. Only V4L2_CID_FOCUS_ABSOLUTE (0..2047) is
supported; image stabilisation is not.

Tested on an LG G8x ThinQ: sweeping the position visibly moves focus
on the IMX363 main camera.

This was written and tested with the help of an AI coding assistant
(Claude Opus 5.5, Anthropic); I reviewed the change and the results.

Signed-off-by: Gaurav Suman <gauravsuman007@gmail.com>
---
 MAINTAINERS                  |   7 +++
 drivers/media/i2c/Kconfig    |  10 ++++
 drivers/media/i2c/Makefile   |   1 +
 drivers/media/i2c/lc898123.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 156 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b13cf3ad43a185802b31a89176fd85d5ba2c6d1b..ab3d82598865973241112a0e5636e3679ea8d35d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20984,6 +20984,13 @@ S:	Maintained
 T:	git git://linuxtv.org/media.git
 F:	drivers/media/i2c/ov9734.c
 
+ON SEMICONDUCTOR LC898123 LENS DRIVER
+M:	Gaurav Suman <gauravsuman007@gmail.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/onnn,lc898123f40.yaml
+F:	drivers/media/i2c/lc898123.c
+
 ONBOARD USB HUB DRIVER
 M:	Matthias Kaehlcke <mka@chromium.org>
 L:	linux-usb@vger.kernel.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 2477db9a17fac4d5278e2428f9b840a0698e56af..fa4d26a27381bd23eafc88dd0af0facbe2fc8ac4 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1022,6 +1022,16 @@ config VIDEO_DW9807_VCM
 	  capability. This is designed for linear control of
 	  voice coil motors, controlled via I2C serial interface.
 
+config VIDEO_LC898123
+	tristate "LC898123F40 lens autofocus support"
+	select V4L2_CCI_I2C
+	help
+	  This is a driver for the autofocus function of the ON
+	  Semiconductor LC898123F40 optical image stabilisation and
+	  autofocus controller, controlled via I2C serial interface.
+	  Only the lens position is controlled; image stabilisation is
+	  not supported. The controller runs firmware from its own flash.
+
 endif
 
 menu "Flash devices"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 7e714dbcbf69d55576be1abe76311c3478511201..836bf5b8a44e260567b34977b191c1f0f6e3667e 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_DW9714) += dw9714.o
 obj-$(CONFIG_VIDEO_DW9719) += dw9719.o
 obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
 obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
+obj-$(CONFIG_VIDEO_LC898123) += lc898123.o
 obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
 obj-$(CONFIG_VIDEO_GC0308) += gc0308.o
 obj-$(CONFIG_VIDEO_GC0310) += gc0310.o
diff --git a/drivers/media/i2c/lc898123.c b/drivers/media/i2c/lc898123.c
new file mode 100644
index 0000000000000000000000000000000000000000..69516ad964d6035e8b2db91ad9fc9c95fa8c9b80
--- /dev/null
+++ b/drivers/media/i2c/lc898123.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ON Semiconductor LC898123F40 OIS/AF controller, autofocus part only
+ * (LG G8x rear main camera). The controller runs its own factory-flashed
+ * firmware; the lens position is a 32-bit big-endian value in 0xF01A and
+ * 0xF012 = 1 enables the servo, as LG's CamX module data does.
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define LC898123_REG_SERVO	CCI_REG32(0xf012)
+#define LC898123_REG_AF_POS	CCI_REG32(0xf01a)
+#define LC898123_REG_OIS_CTRL	CCI_REG8(0xf100)
+#define LC898123_POS_MAX	2047
+#define LC898123_POS_DEFAULT	1024
+
+struct lc898123 {
+	struct regmap *regmap;
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler ctrls;
+	struct v4l2_ctrl *focus;
+};
+
+static int lc898123_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct lc898123 *lc = container_of(ctrl->handler, struct lc898123, ctrls);
+
+	if (ctrl->id != V4L2_CID_FOCUS_ABSOLUTE)
+		return -EINVAL;
+	return cci_write(lc->regmap, LC898123_REG_AF_POS, ctrl->val, NULL);
+}
+
+static const struct v4l2_ctrl_ops lc898123_ctrl_ops = { .s_ctrl = lc898123_set_ctrl };
+
+static int lc898123_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct lc898123 *lc = container_of(sd, struct lc898123, sd);
+	int ret = 0;
+
+	cci_write(lc->regmap, LC898123_REG_OIS_CTRL, 0, &ret);
+	cci_write(lc->regmap, LC898123_REG_SERVO, 1, &ret);
+	if (!ret)
+		ret = __v4l2_ctrl_handler_setup(&lc->ctrls);
+	return ret;
+}
+
+static const struct v4l2_subdev_internal_ops lc898123_internal_ops = {
+	.open = lc898123_open,
+};
+
+static const struct v4l2_subdev_ops lc898123_ops = { };
+
+static int lc898123_probe(struct i2c_client *client)
+{
+	struct lc898123 *lc;
+	u64 val;
+	int ret;
+
+	lc = devm_kzalloc(&client->dev, sizeof(*lc), GFP_KERNEL);
+	if (!lc)
+		return -ENOMEM;
+	ret = devm_regulator_get_enable_optional(&client->dev, "vdd");
+	if (ret && ret != -ENODEV)
+		return dev_err_probe(&client->dev, ret, "vdd supply\n");
+	if (!ret)
+		usleep_range(10000, 12000);	/* controller boot from flash */
+
+	lc->regmap = devm_cci_regmap_init_i2c(client, 16);
+	if (IS_ERR(lc->regmap))
+		return PTR_ERR(lc->regmap);
+	ret = cci_read(lc->regmap, LC898123_REG_AF_POS, &val, NULL);
+	if (ret)
+		return dev_err_probe(&client->dev, ret, "no response\n");
+	cci_write(lc->regmap, LC898123_REG_SERVO, 1, NULL);
+
+	v4l2_i2c_subdev_init(&lc->sd, client, &lc898123_ops);
+	lc->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	lc->sd.internal_ops = &lc898123_internal_ops;
+	lc->sd.entity.function = MEDIA_ENT_F_LENS;
+
+	v4l2_ctrl_handler_init(&lc->ctrls, 1);
+	lc->focus = v4l2_ctrl_new_std(&lc->ctrls, &lc898123_ctrl_ops,
+				      V4L2_CID_FOCUS_ABSOLUTE, 0, LC898123_POS_MAX,
+				      1, LC898123_POS_DEFAULT);
+	if (lc->ctrls.error) {
+		ret = lc->ctrls.error;
+		goto err;
+	}
+	lc->sd.ctrl_handler = &lc->ctrls;
+	ret = media_entity_pads_init(&lc->sd.entity, 0, NULL);
+	if (ret)
+		goto err;
+	ret = v4l2_async_register_subdev(&lc->sd);
+	if (ret)
+		goto err_entity;
+	return 0;
+
+err_entity:
+	media_entity_cleanup(&lc->sd.entity);
+err:
+	v4l2_ctrl_handler_free(&lc->ctrls);
+	return ret;
+}
+
+static void lc898123_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct lc898123 *lc = container_of(sd, struct lc898123, sd);
+
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(&lc->ctrls);
+}
+
+static const struct of_device_id lc898123_of_match[] = {
+	{ .compatible = "onnn,lc898123f40" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lc898123_of_match);
+
+static struct i2c_driver lc898123_driver = {
+	.driver = {
+		.name = "lc898123",
+		.of_match_table = lc898123_of_match,
+	},
+	.probe = lc898123_probe,
+	.remove = lc898123_remove,
+};
+module_i2c_driver(lc898123_driver);
+
+MODULE_DESCRIPTION("LC898123F40 autofocus driver");
+MODULE_LICENSE("GPL");

-- 
2.49.1



      parent reply	other threads:[~2026-11-09 22:38 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09 22:38 [PATCH 0/8] Add SM8150 CAMSS and LG G8x ThinQ camera sensors Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 1/8] dt-bindings: media: Add qcom,sm8150-camss Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 2/8] media: qcom: camss: Add SM8150 support Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 3/8] dt-bindings: media: i2c: Add Samsung S5K3M3 image sensor Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 4/8] media: i2c: Add Samsung S5K3M3 image sensor driver Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 5/8] dt-bindings: media: i2c: Add Samsung S5KGD1 image sensor Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 6/8] media: i2c: Add Samsung S5KGD1 image sensor driver Gaurav Suman via B4 Relay
2026-11-09 22:38 ` [PATCH 7/8] dt-bindings: media: i2c: Add ON Semiconductor LC898123F40 Gaurav Suman via B4 Relay
2026-11-09 22:38 ` Gaurav Suman via B4 Relay [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=20261109-sm8150-camera-v1-8-3d2e7b407516@gmail.com \
    --to=devnull+gauravsuman007.gmail.com@kernel.org \
    --cc=bod@kernel.org \
    --cc=bryan.odonoghue@linaro.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gauravsuman007@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=loic.poulain@oss.qualcomm.com \
    --cc=mchehab@kernel.org \
    --cc=robh@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    --cc=vladimir.zapolskiy@linaro.org \
    /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

all inboxes | Powered by JetHome®