linux-surface/patches/6.2/0012-cameras.patch
Maximilian Luz 36d8905c01
Update v6.2 patches
Changes:
 - Update IPTS driver:
   - Fix redefinition error on AOSP clang
   - Increase the polling frequency to reduce latency
   - Don't allocate a new buffer for every HID report
   - Always use the generic HID driver instead of forcing hid-multitouch
 - Rebase onto v6.1.26

Links:
 - kernel: a317c58533
2023-04-28 19:03:40 +02:00

2944 lines
94 KiB
Diff

From 840b8ef6257ac159e19c3581daaa82f05ad68708 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Sun, 10 Oct 2021 20:56:57 +0200
Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
INT3472 device
The clk and regulator frameworks expect clk/regulator consumer-devices
to have info about the consumed clks/regulators described in the device's
fw_node.
To work around cases where this info is not present in the firmware tables,
which is often the case on x86/ACPI devices, both frameworks allow the
provider-driver to attach info about consumers to the clks/regulators
when registering these.
This causes problems with the probe ordering wrt drivers for consumers
of these clks/regulators. Since the lookups are only registered when the
provider-driver binds, trying to get these clks/regulators before then
results in a -ENOENT error for clks and a dummy regulator for regulators.
One case where we hit this issue is camera sensors such as e.g. the OV8865
sensor found on the Microsoft Surface Go. The sensor uses clks, regulators
and GPIOs provided by a TPS68470 PMIC which is described in an INT3472
ACPI device. There is special platform code handling this and setting
platform_data with the necessary consumer info on the MFD cells
instantiated for the PMIC under: drivers/platform/x86/intel/int3472.
For this to work properly the ov8865 driver must not bind to the I2C-client
for the OV8865 sensor until after the TPS68470 PMIC gpio, regulator and
clk MFD cells have all been fully setup.
The OV8865 on the Microsoft Surface Go is just one example, all X86
devices using the Intel IPU3 camera block found on recent Intel SoCs
have similar issues where there is an INT3472 HID ACPI-device, which
describes the clks and regulators, and the driver for this INT3472 device
must be fully initialized before the sensor driver (any sensor driver)
binds for things to work properly.
On these devices the ACPI nodes describing the sensors all have a _DEP
dependency on the matching INT3472 ACPI device (there is one per sensor).
This allows solving the probe-ordering problem by delaying the enumeration
(instantiation of the I2C-client in the ov8865 example) of ACPI-devices
which have a _DEP dependency on an INT3472 device.
The new acpi_dev_ready_for_enumeration() helper used for this is also
exported because for devices, which have the enumeration_by_parent flag
set, the parent-driver will do its own scan of child ACPI devices and
it will try to enumerate those during its probe(). Code doing this such
as e.g. the i2c-core-acpi.c code must call this new helper to ensure
that it too delays the enumeration until all the _DEP dependencies are
met on devices which have the new honor_deps flag set.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/acpi/scan.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 0c6f06abe3f47..4fc320f424e8e 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2106,6 +2106,9 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
static void acpi_default_enumeration(struct acpi_device *device)
{
+ if (!acpi_dev_ready_for_enumeration(device))
+ return;
+
/*
* Do not enumerate devices with enumeration_by_parent flag set as
* they will be enumerated by their respective parents.
--
2.40.1
From 076e1b192c897f379180ea28ac09ed3551ae03a2 Mon Sep 17 00:00:00 2001
From: zouxiaoh <xiaohong.zou@intel.com>
Date: Fri, 25 Jun 2021 08:52:59 +0800
Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
Intel IPU(Image Processing Unit) has its own (IO)MMU hardware,
The IPU driver allocates its own page table that is not mapped
via the DMA, and thus the Intel IOMMU driver blocks access giving
this error: DMAR: DRHD: handling fault status reg 3 DMAR:
[DMA Read] Request device [00:05.0] PASID ffffffff
fault addr 76406000 [fault reason 06] PTE Read access is not set
As IPU is not an external facing device which is not risky, so use
IOMMU passthrough mode for Intel IPUs.
Change-Id: I6dcccdadac308cf42e20a18e1b593381391e3e6b
Depends-On: Iacd67578e8c6a9b9ac73285f52b4081b72fb68a6
Tracked-On: #JIITL8-411
Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
Signed-off-by: zouxiaoh <xiaohong.zou@intel.com>
Signed-off-by: Xu Chongyang <chongyang.xu@intel.com>
Patchset: cameras
---
drivers/iommu/intel/iommu.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 08e35f9e67a62..a8f20384dfd4b 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -37,6 +37,12 @@
#define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
#define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB)
#define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
+#define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \
+ ((pdev)->device == 0x9a19 || \
+ (pdev)->device == 0x9a39 || \
+ (pdev)->device == 0x4e19 || \
+ (pdev)->device == 0x465d || \
+ (pdev)->device == 0x1919))
#define IS_IPTS(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \
((pdev)->device == 0x9d3e))
#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
@@ -290,12 +296,14 @@ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
static int dmar_map_gfx = 1;
static int dmar_map_ipts = 1;
+static int dmar_map_ipu = 1;
static int intel_iommu_superpage = 1;
static int iommu_identity_mapping;
static int iommu_skip_te_disable;
#define IDENTMAP_GFX 2
#define IDENTMAP_AZALIA 4
+#define IDENTMAP_IPU 8
#define IDENTMAP_IPTS 16
const struct iommu_ops intel_iommu_ops;
@@ -2589,6 +2597,9 @@ static int device_def_domain_type(struct device *dev)
if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
return IOMMU_DOMAIN_IDENTITY;
+ if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev))
+ return IOMMU_DOMAIN_IDENTITY;
+
if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
return IOMMU_DOMAIN_IDENTITY;
}
@@ -2980,6 +2991,9 @@ static int __init init_dmars(void)
if (!dmar_map_gfx)
iommu_identity_mapping |= IDENTMAP_GFX;
+ if (!dmar_map_ipu)
+ iommu_identity_mapping |= IDENTMAP_IPU;
+
if (!dmar_map_ipts)
iommu_identity_mapping |= IDENTMAP_IPTS;
@@ -4823,6 +4837,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
dmar_map_gfx = 0;
}
+static void quirk_iommu_ipu(struct pci_dev *dev)
+{
+ if (!IS_INTEL_IPU(dev))
+ return;
+
+ if (risky_device(dev))
+ return;
+
+ pci_info(dev, "Passthrough IOMMU for integrated Intel IPU\n");
+ dmar_map_ipu = 0;
+}
+
static void quirk_iommu_ipts(struct pci_dev *dev)
{
if (!IS_IPTS(dev))
@@ -4834,6 +4860,7 @@ static void quirk_iommu_ipts(struct pci_dev *dev)
pci_info(dev, "Passthrough IOMMU for IPTS\n");
dmar_map_ipts = 0;
}
+
/* G4x/GM45 integrated gfx dmar support is totally busted. */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
@@ -4869,6 +4896,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
+/* disable IPU dmar support */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_iommu_ipu);
+
/* disable IPTS dmar support */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
--
2.40.1
From e8cef5a3d60c801a1857282cc350bef940b02004 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 10 Oct 2021 20:57:02 +0200
Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
The TPS68470 PMIC has an I2C passthrough mode through which I2C traffic
can be forwarded to a device connected to the PMIC as though it were
connected directly to the system bus. Enable this mode when the chip
is initialised.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/platform/x86/intel/int3472/tps68470.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
index 5b8d1a9620a5d..6a0ff035cf209 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -46,6 +46,13 @@ static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
return ret;
}
+ /* Enable I2C daisy chain */
+ ret = regmap_write(regmap, TPS68470_REG_S_I2C_CTL, 0x03);
+ if (ret) {
+ dev_err(dev, "Failed to enable i2c daisy chain\n");
+ return ret;
+ }
+
dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
return 0;
--
2.40.1
From 87801859ebe76d531f28e30da92570d31f785c67 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 28 Oct 2021 21:55:16 +0100
Subject: [PATCH] media: i2c: Add driver for DW9719 VCM
Add a driver for the DW9719 VCM. The driver creates a v4l2 subdevice
and registers a control to set the desired focus.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
MAINTAINERS | 7 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/dw9719.c | 425 +++++++++++++++++++++++++++++++++++++
4 files changed, 444 insertions(+)
create mode 100644 drivers/media/i2c/dw9719.c
diff --git a/MAINTAINERS b/MAINTAINERS
index f77188f30210f..164d6078a6a32 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6362,6 +6362,13 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.yaml
F: drivers/media/i2c/dw9714.c
+DONGWOON DW9719 LENS VOICE COIL DRIVER
+M: Daniel Scally <djrscally@gmail.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: drivers/media/i2c/dw9719.c
+
DONGWOON DW9768 LENS VOICE COIL DRIVER
M: Dongchun Zhu <dongchun.zhu@mediatek.com>
L: linux-media@vger.kernel.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 833241897d637..8cfd7b6c4bf54 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -847,6 +847,17 @@ config VIDEO_DW9714
capability. This is designed for linear control of
voice coil motors, controlled via I2C serial interface.
+config VIDEO_DW9719
+ tristate "DW9719 lens voice coil support"
+ depends on I2C && VIDEO_V4L2
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_ASYNC
+ help
+ This is a driver for the DW9719 camera lens voice coil.
+ This is designed for linear control of voice coil motors,
+ controlled via I2C serial interface.
+
config VIDEO_DW9768
tristate "DW9768 lens voice coil support"
depends on I2C && VIDEO_DEV
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 4d6c052bb5a7f..29e4d61ce310f 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
obj-$(CONFIG_VIDEO_CX25840) += cx25840/
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_ET8EK8) += et8ek8/
diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
new file mode 100644
index 0000000000000..180b04d2a6b3a
--- /dev/null
+++ b/drivers/media/i2c/dw9719.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2012 Intel Corporation
+
+/*
+ * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo:
+ * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define DW9719_MAX_FOCUS_POS 1023
+#define DW9719_CTRL_STEPS 16
+#define DW9719_CTRL_DELAY_US 1000
+#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
+
+#define DW9719_INFO 0
+#define DW9719_ID 0xF1
+#define DW9719_CONTROL 2
+#define DW9719_VCM_CURRENT 3
+
+#define DW9719_MODE 6
+#define DW9719_VCM_FREQ 7
+
+#define DW9719_MODE_SAC3 0x40
+#define DW9719_DEFAULT_VCM_FREQ 0x60
+#define DW9719_ENABLE_RINGING 0x02
+
+#define NUM_REGULATORS 2
+
+#define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
+
+struct dw9719_device {
+ struct device *dev;
+ struct i2c_client *client;
+ struct regulator_bulk_data regulators[NUM_REGULATORS];
+ struct v4l2_subdev sd;
+
+ struct dw9719_v4l2_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *focus;
+ } ctrls;
+};
+
+static int dw9719_i2c_rd8(struct i2c_client *client, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2] = { reg };
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = buf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &buf[1];
+ *val = 0;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ *val = buf[1];
+
+ return 0;
+}
+
+static int dw9719_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
+{
+ struct i2c_msg msg;
+ int ret;
+
+ u8 buf[2] = { reg, val };
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int dw9719_i2c_wr16(struct i2c_client *client, u8 reg, u16 val)
+{
+ struct i2c_msg msg;
+ u8 buf[3] = { reg };
+ int ret;
+
+ put_unaligned_be16(val, buf + 1);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int dw9719_detect(struct dw9719_device *dw9719)
+{
+ int ret;
+ u8 val;
+
+ ret = dw9719_i2c_rd8(dw9719->client, DW9719_INFO, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != DW9719_ID) {
+ dev_err(dw9719->dev, "Failed to detect correct id\n");
+ ret = -ENXIO;
+ }
+
+ return 0;
+}
+
+static int dw9719_power_down(struct dw9719_device *dw9719)
+{
+ return regulator_bulk_disable(NUM_REGULATORS, dw9719->regulators);
+}
+
+static int dw9719_power_up(struct dw9719_device *dw9719)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(NUM_REGULATORS, dw9719->regulators);
+ if (ret)
+ return ret;
+
+ /* Jiggle SCL pin to wake up device */
+ ret = dw9719_i2c_wr8(dw9719->client, DW9719_CONTROL, 1);
+
+ /* Need 100us to transit from SHUTDOWN to STANDBY*/
+ usleep_range(100, 1000);
+
+ ret = dw9719_i2c_wr8(dw9719->client, DW9719_CONTROL,
+ DW9719_ENABLE_RINGING);
+ if (ret < 0)
+ goto fail_powerdown;
+
+ ret = dw9719_i2c_wr8(dw9719->client, DW9719_MODE, DW9719_MODE_SAC3);
+ if (ret < 0)
+ goto fail_powerdown;
+
+ ret = dw9719_i2c_wr8(dw9719->client, DW9719_VCM_FREQ,
+ DW9719_DEFAULT_VCM_FREQ);
+ if (ret < 0)
+ goto fail_powerdown;
+
+ return 0;
+
+fail_powerdown:
+ dw9719_power_down(dw9719);
+ return ret;
+}
+
+static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
+{
+ int ret;
+
+ value = clamp(value, 0, DW9719_MAX_FOCUS_POS);
+ ret = dw9719_i2c_wr16(dw9719->client, DW9719_VCM_CURRENT, value);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct dw9719_device *dw9719 = container_of(ctrl->handler,
+ struct dw9719_device,
+ ctrls.handler);
+ int ret;
+
+ /* Only apply changes to the controls if the device is powered up */
+ if (!pm_runtime_get_if_in_use(dw9719->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FOCUS_ABSOLUTE:
+ ret = dw9719_t_focus_abs(dw9719, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ pm_runtime_put(dw9719->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
+ .s_ctrl = dw9719_set_ctrl,
+};
+
+static int __maybe_unused dw9719_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct dw9719_device *dw9719 = to_dw9719_device(sd);
+ int ret;
+ int val;
+
+ for (val = dw9719->ctrls.focus->val; val >= 0;
+ val -= DW9719_CTRL_STEPS) {
+ ret = dw9719_t_focus_abs(dw9719, val);
+ if (ret)
+ return ret;
+
+ usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
+ }
+
+ return dw9719_power_down(dw9719);
+}
+
+static int __maybe_unused dw9719_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct dw9719_device *dw9719 = to_dw9719_device(sd);
+ int current_focus = dw9719->ctrls.focus->val;
+ int ret;
+ int val;
+
+ ret = dw9719_power_up(dw9719);
+ if (ret)
+ return ret;
+
+ for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus;
+ val += DW9719_CTRL_STEPS) {
+ ret = dw9719_t_focus_abs(dw9719, val);
+ if (ret)
+ goto err_power_down;
+
+ usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
+ }
+
+ return 0;
+
+err_power_down:
+ dw9719_power_down(dw9719);
+ return ret;
+}
+
+static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return pm_runtime_resume_and_get(sd->dev);
+}
+
+static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ pm_runtime_put(sd->dev);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
+ .open = dw9719_open,
+ .close = dw9719_close,
+};
+
+static int dw9719_init_controls(struct dw9719_device *dw9719)
+{
+ const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
+ if (ret)
+ return ret;
+
+ dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops,
+ V4L2_CID_FOCUS_ABSOLUTE, 0,
+ DW9719_MAX_FOCUS_POS, 1, 0);
+
+ if (dw9719->ctrls.handler.error) {
+ dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n");
+ ret = dw9719->ctrls.handler.error;
+ goto err_free_handler;
+ }
+
+ dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
+
+ return ret;
+
+err_free_handler:
+ v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
+ return ret;
+}
+
+static const struct v4l2_subdev_ops dw9719_ops = { };
+
+static int dw9719_probe(struct i2c_client *client)
+{
+ struct dw9719_device *dw9719;
+ int ret;
+
+ dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL);
+ if (!dw9719)
+ return -ENOMEM;
+
+ dw9719->client = client;
+ dw9719->dev = &client->dev;
+
+ dw9719->regulators[0].supply = "vdd";
+ /*
+ * The DW9719 has only the 1 VDD voltage input, but some PMICs such as
+ * the TPS68470 PMIC have I2C passthrough capability, to disconnect the
+ * sensor's I2C pins from the I2C bus when the sensors VSIO (Sensor-IO)
+ * is off, because some sensors then short these pins to ground;
+ * and the DW9719 might sit behind this passthrough, this it needs to
+ * enable VSIO as that will also enable the I2C passthrough.
+ */
+ dw9719->regulators[1].supply = "vsio";
+
+ ret = devm_regulator_bulk_get(&client->dev, NUM_REGULATORS,
+ dw9719->regulators);
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "getting regulators\n");
+
+ v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops);
+ dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ dw9719->sd.internal_ops = &dw9719_internal_ops;
+
+ ret = dw9719_init_controls(dw9719);
+ if (ret)
+ return ret;
+
+ ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL);
+ if (ret < 0)
+ goto err_free_ctrl_handler;
+
+ dw9719->sd.entity.function = MEDIA_ENT_F_LENS;
+
+ /*
+ * We need the driver to work in the event that pm runtime is disable in
+ * the kernel, so power up and verify the chip now. In the event that
+ * runtime pm is disabled this will leave the chip on, so that the lens
+ * will work.
+ */
+
+ ret = dw9719_power_up(dw9719);
+ if (ret)
+ goto err_cleanup_media;
+
+ ret = dw9719_detect(dw9719);
+ if (ret)
+ goto err_powerdown;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_get_noresume(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ ret = v4l2_async_register_subdev(&dw9719->sd);
+ if (ret < 0)
+ goto err_pm_runtime;
+
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
+
+ return ret;
+
+err_pm_runtime:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+err_powerdown:
+ dw9719_power_down(dw9719);
+err_cleanup_media:
+ media_entity_cleanup(&dw9719->sd.entity);
+err_free_ctrl_handler:
+ v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
+
+ return ret;
+}
+
+static void dw9719_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct dw9719_device *dw9719 = container_of(sd, struct dw9719_device,
+ sd);
+
+ pm_runtime_disable(&client->dev);
+ v4l2_async_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
+ media_entity_cleanup(&dw9719->sd.entity);
+}
+
+static const struct i2c_device_id dw9719_id_table[] = {
+ { "dw9719" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
+
+static const struct dev_pm_ops dw9719_pm_ops = {
+ SET_RUNTIME_PM_OPS(dw9719_suspend, dw9719_resume, NULL)
+};
+
+static struct i2c_driver dw9719_i2c_driver = {
+ .driver = {
+ .name = "dw9719",
+ .pm = &dw9719_pm_ops,
+ },
+ .probe_new = dw9719_probe,
+ .remove = dw9719_remove,
+ .id_table = dw9719_id_table,
+};
+module_i2c_driver(dw9719_i2c_driver);
+
+MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
+MODULE_DESCRIPTION("DW9719 VCM Driver");
+MODULE_LICENSE("GPL");
--
2.40.1
From a3fe98866c446e906264de3425596864853ab0f5 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 4 May 2022 23:21:45 +0100
Subject: [PATCH] media: ipu3-cio2: Move functionality from .complete() to
.bound()
Creating links and registering subdev nodes during the .complete()
callback has the unfortunate effect of preventing all cameras that
connect to a notifier from working if any one of their drivers fails
to probe. Moving the functionality from .complete() to .bound() allows
those camera sensor drivers that did probe correctly to work regardless.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 65 +++++++------------
1 file changed, 23 insertions(+), 42 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index 3b76a9d0383a8..38f9f4da1922e 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1383,7 +1383,10 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
{
struct cio2_device *cio2 = to_cio2_device(notifier);
struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
+ struct device *dev = &cio2->pci_dev->dev;
struct cio2_queue *q;
+ unsigned int pad;
+ int ret;
if (cio2->queue[s_asd->csi2.port].sensor)
return -EBUSY;
@@ -1394,7 +1397,26 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
q->sensor = sd;
q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port);
- return 0;
+ for (pad = 0; pad < q->sensor->entity.num_pads; pad++)
+ if (q->sensor->entity.pads[pad].flags &
+ MEDIA_PAD_FL_SOURCE)
+ break;
+
+ if (pad == q->sensor->entity.num_pads) {
+ dev_err(dev, "failed to find src pad for %s\n",
+ q->sensor->name);
+ return -ENXIO;
+ }
+
+ ret = media_create_pad_link(&q->sensor->entity, pad, &q->subdev.entity,
+ CIO2_PAD_SINK, 0);
+ if (ret) {
+ dev_err(dev, "failed to create link for %s\n",
+ q->sensor->name);
+ return ret;
+ }
+
+ return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
}
/* The .unbind callback */
@@ -1408,50 +1430,9 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
cio2->queue[s_asd->csi2.port].sensor = NULL;
}
-/* .complete() is called after all subdevices have been located */
-static int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
-{
- struct cio2_device *cio2 = to_cio2_device(notifier);
- struct device *dev = &cio2->pci_dev->dev;
- struct sensor_async_subdev *s_asd;
- struct v4l2_async_subdev *asd;
- struct cio2_queue *q;
- unsigned int pad;
- int ret;
-
- list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) {
- s_asd = to_sensor_asd(asd);
- q = &cio2->queue[s_asd->csi2.port];
-
- for (pad = 0; pad < q->sensor->entity.num_pads; pad++)
- if (q->sensor->entity.pads[pad].flags &
- MEDIA_PAD_FL_SOURCE)
- break;
-
- if (pad == q->sensor->entity.num_pads) {
- dev_err(dev, "failed to find src pad for %s\n",
- q->sensor->name);
- return -ENXIO;
- }
-
- ret = media_create_pad_link(
- &q->sensor->entity, pad,
- &q->subdev.entity, CIO2_PAD_SINK,
- 0);
- if (ret) {
- dev_err(dev, "failed to create link for %s\n",
- q->sensor->name);
- return ret;
- }
- }
-
- return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
-}
-
static const struct v4l2_async_notifier_operations cio2_async_ops = {
.bound = cio2_notifier_bound,
.unbind = cio2_notifier_unbind,
- .complete = cio2_notifier_complete,
};
static int cio2_parse_firmware(struct cio2_device *cio2)
--
2.40.1
From c228adc9d76d237a0212e5c4d22bbe56614704a7 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 2 Jun 2022 22:15:56 +0100
Subject: [PATCH] media: ipu3-cio2: Re-add .complete() to ipu3-cio2
Removing the .complete() callback had some unintended consequences.
Because the VCM driver is not directly linked to the ipu3-cio2
driver .bound() never gets called for it, which means its devnode
is never created if it probes late. Because .complete() waits for
any sub-notifiers to also be complete it is captured in that call.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index 38f9f4da1922e..82681df7d794f 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1430,9 +1430,18 @@ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
cio2->queue[s_asd->csi2.port].sensor = NULL;
}
+/* .complete() is called after all subdevices have been located */
+static int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct cio2_device *cio2 = to_cio2_device(notifier);
+
+ return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
+}
+
static const struct v4l2_async_notifier_operations cio2_async_ops = {
.bound = cio2_notifier_bound,
.unbind = cio2_notifier_unbind,
+ .complete = cio2_notifier_complete,
};
static int cio2_parse_firmware(struct cio2_device *cio2)
--
2.40.1
From 37017dca3d8ea8fd8482fa6ae49e4576fe8016be Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Fri, 15 Jul 2022 23:48:00 +0200
Subject: [PATCH] drivers/media/i2c: Fix DW9719 dependencies
It should depend on VIDEO_DEV instead of VIDEO_V4L2.
Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
Patchset: cameras
---
drivers/media/i2c/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 8cfd7b6c4bf54..11b8acd7cc5fe 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -849,7 +849,7 @@ config VIDEO_DW9714
config VIDEO_DW9719
tristate "DW9719 lens voice coil support"
- depends on I2C && VIDEO_V4L2
+ depends on I2C && VIDEO_DEV
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_ASYNC
--
2.40.1
From bb5d1fa9d8412da365e5eb39b15228ca2efc5fbb Mon Sep 17 00:00:00 2001
From: Daniel Scally <dan.scally@ideasonboard.com>
Date: Thu, 2 Mar 2023 12:59:39 +0000
Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E
ACPI _HID INT347E represents the OmniVision 7251 camera sensor. The
driver for this sensor expects a single pin named "enable", but on
some Microsoft Surface platforms the sensor is assigned a single
GPIO who's type flag is INT3472_GPIO_TYPE_RESET.
Remap the GPIO pin's function from "reset" to "enable". This is done
outside of the existing remap table since it is a more widespread
discrepancy than that method is designed for. Additionally swap the
polarity of the pin to match the driver's expectation.
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Patchset: cameras
---
drivers/platform/x86/intel/int3472/discrete.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index c42c3faa2c32d..6f4b8e24eb56c 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -108,6 +108,9 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
{
const struct int3472_sensor_config *sensor_config;
char *path = agpio->resource_source.string_ptr;
+ const struct acpi_device_id ov7251_ids[] = {
+ { "INT347E" },
+ };
struct gpiod_lookup *table_entry;
struct acpi_device *adev;
acpi_handle handle;
@@ -130,6 +133,17 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
}
}
+ /*
+ * In addition to the function remap table we need to bulk remap the
+ * "reset" GPIO for the OmniVision 7251 sensor, as the driver for that
+ * expects its only GPIO pin to be called "enable" (and to have the
+ * opposite polarity).
+ */
+ if (!strcmp(func, "reset") && !acpi_match_device_ids(int3472->sensor, ov7251_ids)) {
+ func = "enable";
+ polarity = GPIO_ACTIVE_HIGH;
+ }
+
/* Functions mapped to NULL should not be mapped to the sensor */
if (!func)
return 0;
--
2.40.1
From 7fadb6de757a772654518be2cdfe231d0ed6080b Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 20 Jan 2023 12:45:15 +0100
Subject: [PATCH] leds: led-class: Add led_module_get() helper
Split out part of of_led_get() into a generic led_module_get() helper
function.
This is a preparation patch for adding a generic (non devicetree specific)
led_get() function.
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/leds/led-class.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index aa39b2a48fdff..743d97b082dcb 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -215,6 +215,23 @@ static int led_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
+static struct led_classdev *led_module_get(struct device *led_dev)
+{
+ struct led_classdev *led_cdev;
+
+ if (!led_dev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ led_cdev = dev_get_drvdata(led_dev);
+
+ if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
+ put_device(led_cdev->dev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ return led_cdev;
+}
+
/**
* of_led_get() - request a LED device via the LED framework
* @np: device node to get the LED device from
@@ -226,7 +243,6 @@ static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
struct led_classdev *of_led_get(struct device_node *np, int index)
{
struct device *led_dev;
- struct led_classdev *led_cdev;
struct device_node *led_node;
led_node = of_parse_phandle(np, "leds", index);
@@ -235,19 +251,8 @@ struct led_classdev *of_led_get(struct device_node *np, int index)
led_dev = class_find_device_by_of_node(leds_class, led_node);
of_node_put(led_node);
- put_device(led_dev);
-
- if (!led_dev)
- return ERR_PTR(-EPROBE_DEFER);
-
- led_cdev = dev_get_drvdata(led_dev);
- if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
- put_device(led_cdev->dev);
- return ERR_PTR(-ENODEV);
- }
-
- return led_cdev;
+ return led_module_get(led_dev);
}
EXPORT_SYMBOL_GPL(of_led_get);
--
2.40.1
From dbe347cff55928d6e5c341b15ad3c50209e6df1c Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 20 Jan 2023 12:45:16 +0100
Subject: [PATCH] leds: led-class: Add __devm_led_get() helper
Add a __devm_led_get() helper which registers a passed in led_classdev
with devm for unregistration.
This is a preparation patch for adding a generic (non devicetree specific)
devm_led_get() function.
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/leds/led-class.c | 29 +++++++++++++++++------------
1 file changed, 17 insertions(+), 12 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 743d97b082dcb..4904d140a560a 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -274,6 +274,22 @@ static void devm_led_release(struct device *dev, void *res)
led_put(*p);
}
+static struct led_classdev *__devm_led_get(struct device *dev, struct led_classdev *led)
+{
+ struct led_classdev **dr;
+
+ dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *), GFP_KERNEL);
+ if (!dr) {
+ led_put(led);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ *dr = led;
+ devres_add(dev, dr);
+
+ return led;
+}
+
/**
* devm_of_led_get - Resource-managed request of a LED device
* @dev: LED consumer
@@ -289,7 +305,6 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
int index)
{
struct led_classdev *led;
- struct led_classdev **dr;
if (!dev)
return ERR_PTR(-EINVAL);
@@ -298,17 +313,7 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
if (IS_ERR(led))
return led;
- dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *),
- GFP_KERNEL);
- if (!dr) {
- led_put(led);
- return ERR_PTR(-ENOMEM);
- }
-
- *dr = led;
- devres_add(dev, dr);
-
- return led;
+ return __devm_led_get(dev, led);
}
EXPORT_SYMBOL_GPL(devm_of_led_get);
--
2.40.1
From 2aaf59bff7ad5a0de2cf0078f6ad8cf96ff83dc8 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 20 Jan 2023 12:45:17 +0100
Subject: [PATCH] leds: led-class: Add generic [devm_]led_get()
Add a generic [devm_]led_get() method which can be used on both devicetree
and non devicetree platforms to get a LED classdev associated with
a specific function on a specific device, e.g. the privacy LED associated
with a specific camera sensor.
Note unlike of_led_get() this takes a string describing the function
rather then an index. This is done because e.g. camera sensors might
have a privacy LED, or a flash LED, or both and using an index
approach leaves it unclear what the function of index 0 is if there is
only 1 LED.
This uses a lookup-table mechanism for non devicetree platforms.
This allows the platform code to map specific LED class_dev-s to a specific
device,function combinations this way.
For devicetree platforms getting the LED by function-name could be made
to work using the standard devicetree pattern of adding a -names string
array to map names to the indexes.
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/leds/led-class.c | 84 ++++++++++++++++++++++++++++++++++++++++
include/linux/leds.h | 21 ++++++++++
2 files changed, 105 insertions(+)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 4904d140a560a..0c4b8d8d2b4fe 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -23,6 +23,8 @@
#include "leds.h"
static struct class *leds_class;
+static DEFINE_MUTEX(leds_lookup_lock);
+static LIST_HEAD(leds_lookup_list);
static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -317,6 +319,88 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_of_led_get);
+/**
+ * led_get() - request a LED device via the LED framework
+ * @dev: device for which to get the LED device
+ * @con_id: name of the LED from the device's point of view
+ *
+ * @return a pointer to a LED device or ERR_PTR(errno) on failure.
+ */
+struct led_classdev *led_get(struct device *dev, char *con_id)
+{
+ struct led_lookup_data *lookup;
+ const char *provider = NULL;
+ struct device *led_dev;
+
+ mutex_lock(&leds_lookup_lock);
+ list_for_each_entry(lookup, &leds_lookup_list, list) {
+ if (!strcmp(lookup->dev_id, dev_name(dev)) &&
+ !strcmp(lookup->con_id, con_id)) {
+ provider = kstrdup_const(lookup->provider, GFP_KERNEL);
+ break;
+ }
+ }
+ mutex_unlock(&leds_lookup_lock);
+
+ if (!provider)
+ return ERR_PTR(-ENOENT);
+
+ led_dev = class_find_device_by_name(leds_class, provider);
+ kfree_const(provider);
+
+ return led_module_get(led_dev);
+}
+EXPORT_SYMBOL_GPL(led_get);
+
+/**
+ * devm_led_get() - request a LED device via the LED framework
+ * @dev: device for which to get the LED device
+ * @con_id: name of the LED from the device's point of view
+ *
+ * The LED device returned from this function is automatically released
+ * on driver detach.
+ *
+ * @return a pointer to a LED device or ERR_PTR(errno) on failure.
+ */
+struct led_classdev *devm_led_get(struct device *dev, char *con_id)
+{
+ struct led_classdev *led;
+
+ led = led_get(dev, con_id);
+ if (IS_ERR(led))
+ return led;
+
+ return __devm_led_get(dev, led);
+}
+EXPORT_SYMBOL_GPL(devm_led_get);
+
+/**
+ * led_add_lookup() - Add a LED lookup table entry
+ * @led_lookup: the lookup table entry to add
+ *
+ * Add a LED lookup table entry. On systems without devicetree the lookup table
+ * is used by led_get() to find LEDs.
+ */
+void led_add_lookup(struct led_lookup_data *led_lookup)
+{
+ mutex_lock(&leds_lookup_lock);
+ list_add_tail(&led_lookup->list, &leds_lookup_list);
+ mutex_unlock(&leds_lookup_lock);
+}
+EXPORT_SYMBOL_GPL(led_add_lookup);
+
+/**
+ * led_remove_lookup() - Remove a LED lookup table entry
+ * @led_lookup: the lookup table entry to remove
+ */
+void led_remove_lookup(struct led_lookup_data *led_lookup)
+{
+ mutex_lock(&leds_lookup_lock);
+ list_del(&led_lookup->list);
+ mutex_unlock(&leds_lookup_lock);
+}
+EXPORT_SYMBOL_GPL(led_remove_lookup);
+
static int led_classdev_next_name(const char *init_name, char *name,
size_t len)
{
diff --git a/include/linux/leds.h b/include/linux/leds.h
index ba4861ec73d30..31cb74b90ffcd 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -39,6 +39,21 @@ enum led_default_state {
LEDS_DEFSTATE_KEEP = 2,
};
+/**
+ * struct led_lookup_data - represents a single LED lookup entry
+ *
+ * @list: internal list of all LED lookup entries
+ * @provider: name of led_classdev providing the LED
+ * @dev_id: name of the device associated with this LED
+ * @con_id: name of the LED from the device's point of view
+ */
+struct led_lookup_data {
+ struct list_head list;
+ const char *provider;
+ const char *dev_id;
+ const char *con_id;
+};
+
struct led_init_data {
/* device fwnode handle */
struct fwnode_handle *fwnode;
@@ -211,6 +226,12 @@ void devm_led_classdev_unregister(struct device *parent,
void led_classdev_suspend(struct led_classdev *led_cdev);
void led_classdev_resume(struct led_classdev *led_cdev);
+void led_add_lookup(struct led_lookup_data *led_lookup);
+void led_remove_lookup(struct led_lookup_data *led_lookup);
+
+struct led_classdev *__must_check led_get(struct device *dev, char *con_id);
+struct led_classdev *__must_check devm_led_get(struct device *dev, char *con_id);
+
extern struct led_classdev *of_led_get(struct device_node *np, int index);
extern void led_put(struct led_classdev *led_cdev);
struct led_classdev *__must_check devm_of_led_get(struct device *dev,
--
2.40.1
From 9032edffe5ae80fb1d384dda4928b87017ae1776 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 20 Jan 2023 12:45:18 +0100
Subject: [PATCH] leds: led-class: Add devicetree support to led_get()
Turn of_led_get() into a more generic __of_led_get() helper function,
which can lookup LEDs in devicetree by either name or index.
And use this new helper to add devicetree support to the generic
(non devicetree specific) [devm_]led_get() function.
This uses the standard devicetree pattern of adding a -names string array
to map names to the indexes for an array of resources.
Note the new led-names property for LED consumers is not added
to the devicetree documentation because there seems to be no
documentation for the leds property itself to extend it with this.
It seems that how LED consumers should be described is not documented
at all ATM.
This patch is marked as RFC because of both the missing devicetree
documentation and because there are no devicetree users of
the generic [devm_]led_get() function for now.
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/leds/led-class.c | 37 ++++++++++++++++++++++++++++---------
1 file changed, 28 insertions(+), 9 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 0c4b8d8d2b4fe..2f3af6e302082 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -234,19 +234,18 @@ static struct led_classdev *led_module_get(struct device *led_dev)
return led_cdev;
}
-/**
- * of_led_get() - request a LED device via the LED framework
- * @np: device node to get the LED device from
- * @index: the index of the LED
- *
- * Returns the LED device parsed from the phandle specified in the "leds"
- * property of a device tree node or a negative error-code on failure.
- */
-struct led_classdev *of_led_get(struct device_node *np, int index)
+static struct led_classdev *__of_led_get(struct device_node *np, int index,
+ const char *name)
{
struct device *led_dev;
struct device_node *led_node;
+ /*
+ * For named LEDs, first look up the name in the "led-names" property.
+ * If it cannot be found, then of_parse_phandle() will propagate the error.
+ */
+ if (name)
+ index = of_property_match_string(np, "led-names", name);
led_node = of_parse_phandle(np, "leds", index);
if (!led_node)
return ERR_PTR(-ENOENT);
@@ -256,6 +255,19 @@ struct led_classdev *of_led_get(struct device_node *np, int index)
return led_module_get(led_dev);
}
+
+/**
+ * of_led_get() - request a LED device via the LED framework
+ * @np: device node to get the LED device from
+ * @index: the index of the LED
+ *
+ * Returns the LED device parsed from the phandle specified in the "leds"
+ * property of a device tree node or a negative error-code on failure.
+ */
+struct led_classdev *of_led_get(struct device_node *np, int index)
+{
+ return __of_led_get(np, index, NULL);
+}
EXPORT_SYMBOL_GPL(of_led_get);
/**
@@ -329,9 +341,16 @@ EXPORT_SYMBOL_GPL(devm_of_led_get);
struct led_classdev *led_get(struct device *dev, char *con_id)
{
struct led_lookup_data *lookup;
+ struct led_classdev *led_cdev;
const char *provider = NULL;
struct device *led_dev;
+ if (dev->of_node) {
+ led_cdev = __of_led_get(dev->of_node, -1, con_id);
+ if (!IS_ERR(led_cdev) || PTR_ERR(led_cdev) != -ENOENT)
+ return led_cdev;
+ }
+
mutex_lock(&leds_lookup_lock);
list_for_each_entry(lookup, &leds_lookup_list, list) {
if (!strcmp(lookup->dev_id, dev_name(dev)) &&
--
2.40.1
From c74a1028b7e427b1798fef1cbd3d2e5df4688cbc Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 20 Jan 2023 12:45:19 +0100
Subject: [PATCH] media: v4l2-core: Built async and fwnode code into
videodev.ko
Currently the videodev.ko code may be builtin while e.g. v4l2-fwnode.ko
is build as a module.
This makes it hard to add code depending on other subsystems spanning
both videodev.ko and v4l2-fwnode.ko. Specifically this block adding code
depending on the LED subsystem.
This is made even harder because CONFIG_V4L2_FWNODE is selected,
not depended on so it itself cannot depend on another subsystem without
editing all the Kconfig symbols selecting it to also list the dependency
and there are many of such symbols.
Adding a "select LED_CLASS if NEW_LEDS" to CONFIG_V4L2_FWNODE leads
to Kconfig erroring out with "error: recursive dependency detected!".
To fix this dependency mess, change the V4L2_FWNODE and V4L2_ASYNC
(which V4L2_FWNODE selects) Kconfig symbols from tristate to bools and
link their code into videodev.ko instead of making them separate modules.
This will allow using IS_REACHABLE(LED_CLASS) for the new LED integration
code without needing to worry that it expands to 0 in some places and
1 in other places because some of the code being builtin vs modular.
On x86_64 this leads to the following size changes for videodev.ko
[hans@shalem linux]$ size drivers/media/v4l2-core/videodev.ko
Before:
text data bss dec hex filename
218206 14395 2448 235049 39629 drivers/media/v4l2-core/videodev.ko
After:
text data bss dec hex filename
243213 17615 2456 263284 40474 drivers/media/v4l2-core/videodev.ko
So (as expected) there is some increase in size here, but it
really is not that much.
And the uncompressed no-debuginfo .ko file disk-usage actually shrinks
by 17 KiB (comparing the slightly larger videodev.ko against the
3 original modules) and loading time will also be better.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/media/v4l2-core/Kconfig | 4 ++--
drivers/media/v4l2-core/Makefile | 4 ++--
drivers/media/v4l2-core/v4l2-async.c | 17 ++++-------------
drivers/media/v4l2-core/v4l2-dev-priv.h | 19 +++++++++++++++++++
drivers/media/v4l2-core/v4l2-dev.c | 8 ++++++++
drivers/media/v4l2-core/v4l2-fwnode.c | 6 ------
6 files changed, 35 insertions(+), 23 deletions(-)
create mode 100644 drivers/media/v4l2-core/v4l2-dev-priv.h
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 348559bc24689..73574d9460105 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -68,11 +68,11 @@ config V4L2_FLASH_LED_CLASS
When in doubt, say N.
config V4L2_FWNODE
- tristate
+ bool
select V4L2_ASYNC
config V4L2_ASYNC
- tristate
+ bool
# Used by drivers that need Videobuf modules
config VIDEOBUF_GEN
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 41d91bd10cf28..8c5a1ab8d939a 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -15,7 +15,9 @@ videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
# Please keep it alphabetically sorted by Kconfig name
# (e. g. LC_ALL=C sort Makefile)
+videodev-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
videodev-$(CONFIG_COMPAT) += v4l2-compat-ioctl32.o
+videodev-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
videodev-$(CONFIG_MEDIA_CONTROLLER) += v4l2-mc.o
videodev-$(CONFIG_SPI) += v4l2-spi.o
videodev-$(CONFIG_TRACEPOINTS) += v4l2-trace.o
@@ -24,9 +26,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
# Please keep it alphabetically sorted by Kconfig name
# (e. g. LC_ALL=C sort Makefile)
-obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
-obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 2f1b718a91893..024d6b82b50af 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -11,7 +11,6 @@
#include <linux/i2c.h>
#include <linux/list.h>
#include <linux/mm.h>
-#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -24,6 +23,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "v4l2-dev-priv.h"
+
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
@@ -900,25 +901,15 @@ DEFINE_SHOW_ATTRIBUTE(pending_subdevs);
static struct dentry *v4l2_async_debugfs_dir;
-static int __init v4l2_async_init(void)
+void __init v4l2_async_debugfs_init(void)
{
v4l2_async_debugfs_dir = debugfs_create_dir("v4l2-async", NULL);
debugfs_create_file("pending_async_subdevices", 0444,
v4l2_async_debugfs_dir, NULL,
&pending_subdevs_fops);
-
- return 0;
}
-static void __exit v4l2_async_exit(void)
+void __exit v4l2_async_debugfs_exit(void)
{
debugfs_remove_recursive(v4l2_async_debugfs_dir);
}
-
-subsys_initcall(v4l2_async_init);
-module_exit(v4l2_async_exit);
-
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
-MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-dev-priv.h b/drivers/media/v4l2-core/v4l2-dev-priv.h
new file mode 100644
index 0000000000000..b5b1ee78be20a
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-dev-priv.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Video capture interface for Linux version 2 private header.
+ *
+ * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _V4L2_DEV_PRIV_H_
+#define _V4L2_DEV_PRIV_H_
+
+#if IS_ENABLED(CONFIG_V4L2_ASYNC)
+void v4l2_async_debugfs_init(void);
+void v4l2_async_debugfs_exit(void);
+#else
+static inline void v4l2_async_debugfs_init(void) {}
+static inline void v4l2_async_debugfs_exit(void) {}
+#endif
+
+#endif
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 397d553177fa7..10ba2e4196a65 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -31,6 +31,8 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
+#include "v4l2-dev-priv.h"
+
#define VIDEO_NUM_DEVICES 256
#define VIDEO_NAME "video4linux"
@@ -1190,6 +1192,7 @@ static int __init videodev_init(void)
return -EIO;
}
+ v4l2_async_debugfs_init();
return 0;
}
@@ -1197,6 +1200,7 @@ static void __exit videodev_exit(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+ v4l2_async_debugfs_exit();
class_unregister(&video_class);
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
}
@@ -1205,6 +1209,10 @@ subsys_initcall(videodev_init);
module_exit(videodev_exit)
MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@kernel.org>, Bill Dirks, Justin Schoeman, Gerd Knorr");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
+MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_DESCRIPTION("Video4Linux2 core driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 3d9533c1b2029..c8a2264262bca 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -17,7 +17,6 @@
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/mm.h>
-#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -1328,8 +1327,3 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
return ret;
}
EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
--
2.40.1
From d7a0becfbce5cb0af5955692b524910d550f376e Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 27 Jan 2023 21:37:25 +0100
Subject: [PATCH] media: v4l2-core: Make the v4l2-core code enable/disable the
privacy LED if present
Make v4l2_async_register_subdev_sensor() try to get a privacy LED
associated with the sensor and extend the call_s_stream() wrapper to
enable/disable the privacy LED if found.
This makes the core handle privacy LED control, rather then having to
duplicate this code in all the sensor drivers.
Suggested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/media/v4l2-core/v4l2-async.c | 3 ++
drivers/media/v4l2-core/v4l2-fwnode.c | 7 ++++
drivers/media/v4l2-core/v4l2-subdev-priv.h | 14 +++++++
drivers/media/v4l2-core/v4l2-subdev.c | 44 ++++++++++++++++++++++
include/media/v4l2-subdev.h | 3 ++
5 files changed, 71 insertions(+)
create mode 100644 drivers/media/v4l2-core/v4l2-subdev-priv.h
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 024d6b82b50af..b26dcb8d423e1 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -24,6 +24,7 @@
#include <media/v4l2-subdev.h>
#include "v4l2-dev-priv.h"
+#include "v4l2-subdev-priv.h"
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev,
@@ -823,6 +824,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
if (!sd->async_list.next)
return;
+ v4l2_subdev_put_privacy_led(sd);
+
mutex_lock(&list_lock);
__v4l2_async_nf_unregister(sd->subdev_notifier);
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index c8a2264262bca..8501b6931d3a0 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -27,6 +27,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "v4l2-subdev-priv.h"
+
static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type;
@@ -1301,6 +1303,10 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_init(notifier);
+ ret = v4l2_subdev_get_privacy_led(sd);
+ if (ret < 0)
+ goto out_cleanup;
+
ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
if (ret < 0)
goto out_cleanup;
@@ -1321,6 +1327,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_unregister(notifier);
out_cleanup:
+ v4l2_subdev_put_privacy_led(sd);
v4l2_async_nf_cleanup(notifier);
kfree(notifier);
diff --git a/drivers/media/v4l2-core/v4l2-subdev-priv.h b/drivers/media/v4l2-core/v4l2-subdev-priv.h
new file mode 100644
index 0000000000000..7ad2812c3d8e3
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-subdev-priv.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * V4L2 sub-device pivate header.
+ *
+ * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _V4L2_SUBDEV_PRIV_H_
+#define _V4L2_SUBDEV_PRIV_H_
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd);
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd);
+
+#endif
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 4988a25bd8f46..9fd1836282859 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -9,6 +9,7 @@
*/
#include <linux/ioctl.h>
+#include <linux/leds.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -23,6 +24,8 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
+#include "v4l2-subdev-priv.h"
+
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
{
@@ -322,6 +325,14 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
{
int ret;
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ if (enable)
+ led_set_brightness(sd->privacy_led, sd->privacy_led->max_brightness);
+ else
+ led_set_brightness(sd->privacy_led, 0);
+ }
+#endif
ret = sd->ops->video->s_stream(sd, enable);
if (!enable && ret < 0) {
@@ -1090,6 +1101,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
sd->grp_id = 0;
sd->dev_priv = NULL;
sd->host_priv = NULL;
+ sd->privacy_led = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name;
sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
@@ -1105,3 +1117,35 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,
v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev);
}
EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event);
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ sd->privacy_led = led_get(sd->dev, "privacy-led");
+ if (IS_ERR(sd->privacy_led) && PTR_ERR(sd->privacy_led) != -ENOENT)
+ return dev_err_probe(sd->dev, PTR_ERR(sd->privacy_led), "getting privacy LED\n");
+
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ mutex_lock(&sd->privacy_led->led_access);
+ led_sysfs_disable(sd->privacy_led);
+ led_trigger_remove(sd->privacy_led);
+ led_set_brightness(sd->privacy_led, 0);
+ mutex_unlock(&sd->privacy_led->led_access);
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_privacy_led);
+
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ mutex_lock(&sd->privacy_led->led_access);
+ led_sysfs_enable(sd->privacy_led);
+ mutex_unlock(&sd->privacy_led->led_access);
+ led_put(sd->privacy_led);
+ }
+#endif
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_put_privacy_led);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index b15fa9930f30c..0547313f98cc4 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -38,6 +38,7 @@ struct v4l2_subdev;
struct v4l2_subdev_fh;
struct tuner_setup;
struct v4l2_mbus_frame_desc;
+struct led_classdev;
/**
* struct v4l2_decode_vbi_line - used to decode_vbi_line
@@ -982,6 +983,8 @@ struct v4l2_subdev {
* appropriate functions.
*/
+ struct led_classdev *privacy_led;
+
/*
* TODO: active_state should most likely be changed from a pointer to an
* embedded field. For the time being it's kept as a pointer to more
--
2.40.1
From c2356434deda19a17f02388a5643591dbcd5450a Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 27 Jan 2023 21:37:26 +0100
Subject: [PATCH] platform/x86: int3472/discrete: Refactor GPIO to sensor
mapping
Add a helper function to map the type returned by the _DSM
method to a function name + the default polarity for that function.
And fold the INT3472_GPIO_TYPE_RESET and INT3472_GPIO_TYPE_POWERDOWN
cases into a single generic case.
This is a preparation patch for further GPIO mapping changes.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/platform/x86/intel/int3472/discrete.c | 45 +++++++++++++++----
1 file changed, 36 insertions(+), 9 deletions(-)
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 6f4b8e24eb56c..443f8dcb1e733 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -202,6 +202,36 @@ static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
return 0;
}
+static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)
+{
+ switch (type) {
+ case INT3472_GPIO_TYPE_RESET:
+ *func = "reset";
+ *polarity = GPIO_ACTIVE_LOW;
+ break;
+ case INT3472_GPIO_TYPE_POWERDOWN:
+ *func = "powerdown";
+ *polarity = GPIO_ACTIVE_LOW;
+ break;
+ case INT3472_GPIO_TYPE_CLK_ENABLE:
+ *func = "clk-enable";
+ *polarity = GPIO_ACTIVE_HIGH;
+ break;
+ case INT3472_GPIO_TYPE_PRIVACY_LED:
+ *func = "privacy-led";
+ *polarity = GPIO_ACTIVE_HIGH;
+ break;
+ case INT3472_GPIO_TYPE_POWER_ENABLE:
+ *func = "power-enable";
+ *polarity = GPIO_ACTIVE_HIGH;
+ break;
+ default:
+ *func = "unknown";
+ *polarity = GPIO_ACTIVE_HIGH;
+ break;
+ }
+}
+
/**
* skl_int3472_handle_gpio_resources: Map PMIC resources to consuming sensor
* @ares: A pointer to a &struct acpi_resource
@@ -241,6 +271,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
struct acpi_resource_gpio *agpio;
union acpi_object *obj;
const char *err_msg;
+ const char *func;
+ u32 polarity;
int ret;
u8 type;
@@ -264,19 +296,14 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
type = obj->integer.value & 0xff;
+ int3472_get_func_and_polarity(type, &func, &polarity);
+
switch (type) {
case INT3472_GPIO_TYPE_RESET:
- ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset",
- GPIO_ACTIVE_LOW);
- if (ret)
- err_msg = "Failed to map reset pin to sensor\n";
-
- break;
case INT3472_GPIO_TYPE_POWERDOWN:
- ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown",
- GPIO_ACTIVE_LOW);
+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity);
if (ret)
- err_msg = "Failed to map powerdown pin to sensor\n";
+ err_msg = "Failed to map GPIO pin to sensor\n";
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
--
2.40.1
From fc088beb4c2dd09f969c9ce4d0c88dc77c4c6c89 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 27 Jan 2023 21:37:27 +0100
Subject: [PATCH] platform/x86: int3472/discrete: Create a LED class device for
the privacy LED
On some systems, e.g. the Lenovo ThinkPad X1 Yoga gen 7 and the ThinkPad
X1 Nano gen 2 there is no clock-enable pin, triggering the:
"No clk GPIO. The privacy LED won't work" warning and causing the privacy
LED to not work.
Fix this by modeling the privacy LED as a LED class device rather then
integrating it with the registered clock.
Note this relies on media subsys changes to actually turn the LED on/off
when the sensor's v4l2_subdev's s_stream() operand gets called.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/platform/x86/intel/int3472/Makefile | 2 +-
.../x86/intel/int3472/clk_and_regulator.c | 3 -
drivers/platform/x86/intel/int3472/common.h | 15 +++-
drivers/platform/x86/intel/int3472/discrete.c | 58 ++++-----------
drivers/platform/x86/intel/int3472/led.c | 74 +++++++++++++++++++
5 files changed, 105 insertions(+), 47 deletions(-)
create mode 100644 drivers/platform/x86/intel/int3472/led.c
diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile
index cfec7784c5c93..9f16cb5143973 100644
--- a/drivers/platform/x86/intel/int3472/Makefile
+++ b/drivers/platform/x86/intel/int3472/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \
intel_skl_int3472_tps68470.o
-intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o common.o
+intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o
intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o
diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
index 74dc2cff799ee..e3b597d933880 100644
--- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c
+++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
@@ -23,8 +23,6 @@ static int skl_int3472_clk_prepare(struct clk_hw *hw)
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
gpiod_set_value_cansleep(clk->ena_gpio, 1);
- gpiod_set_value_cansleep(clk->led_gpio, 1);
-
return 0;
}
@@ -33,7 +31,6 @@ static void skl_int3472_clk_unprepare(struct clk_hw *hw)
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
gpiod_set_value_cansleep(clk->ena_gpio, 0);
- gpiod_set_value_cansleep(clk->led_gpio, 0);
}
static int skl_int3472_clk_enable(struct clk_hw *hw)
diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h
index 53270d19c73ab..82dc37e08882e 100644
--- a/drivers/platform/x86/intel/int3472/common.h
+++ b/drivers/platform/x86/intel/int3472/common.h
@@ -6,6 +6,7 @@
#include <linux/clk-provider.h>
#include <linux/gpio/machine.h>
+#include <linux/leds.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/types.h>
@@ -28,6 +29,8 @@
#define GPIO_REGULATOR_NAME_LENGTH 21
#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
+#define INT3472_LED_MAX_NAME_LEN 32
+
#define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86
#define INT3472_REGULATOR(_name, _supply, _ops) \
@@ -96,10 +99,16 @@ struct int3472_discrete_device {
struct clk_hw clk_hw;
struct clk_lookup *cl;
struct gpio_desc *ena_gpio;
- struct gpio_desc *led_gpio;
u32 frequency;
} clock;
+ struct int3472_pled {
+ struct led_classdev classdev;
+ struct led_lookup_data lookup;
+ char name[INT3472_LED_MAX_NAME_LEN];
+ struct gpio_desc *gpio;
+ } pled;
+
unsigned int ngpios; /* how many GPIOs have we seen */
unsigned int n_sensor_gpios; /* how many have we mapped to sensor */
struct gpiod_lookup_table gpios;
@@ -119,4 +128,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio);
void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472);
+int skl_int3472_register_pled(struct int3472_discrete_device *int3472,
+ struct acpi_resource_gpio *agpio, u32 polarity);
+void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472);
+
#endif
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 443f8dcb1e733..67d26f8a8d9a3 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -169,37 +169,21 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
}
static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
- struct acpi_resource_gpio *agpio, u8 type)
+ struct acpi_resource_gpio *agpio)
{
char *path = agpio->resource_source.string_ptr;
u16 pin = agpio->pin_table[0];
struct gpio_desc *gpio;
- switch (type) {
- case INT3472_GPIO_TYPE_CLK_ENABLE:
- gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable");
- if (IS_ERR(gpio))
- return (PTR_ERR(gpio));
-
- int3472->clock.ena_gpio = gpio;
- /* Ensure the pin is in output mode and non-active state */
- gpiod_direction_output(int3472->clock.ena_gpio, 0);
- break;
- case INT3472_GPIO_TYPE_PRIVACY_LED:
- gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led");
- if (IS_ERR(gpio))
- return (PTR_ERR(gpio));
+ gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable");
+ if (IS_ERR(gpio))
+ return (PTR_ERR(gpio));
- int3472->clock.led_gpio = gpio;
- /* Ensure the pin is in output mode and non-active state */
- gpiod_direction_output(int3472->clock.led_gpio, 0);
- break;
- default:
- dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type);
- break;
- }
+ int3472->clock.ena_gpio = gpio;
+ /* Ensure the pin is in output mode and non-active state */
+ gpiod_direction_output(int3472->clock.ena_gpio, 0);
- return 0;
+ return skl_int3472_register_clock(int3472);
}
static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)
@@ -307,11 +291,16 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
- case INT3472_GPIO_TYPE_PRIVACY_LED:
- ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type);
+ ret = skl_int3472_map_gpio_to_clk(int3472, agpio);
if (ret)
err_msg = "Failed to map GPIO to clock\n";
+ break;
+ case INT3472_GPIO_TYPE_PRIVACY_LED:
+ ret = skl_int3472_register_pled(int3472, agpio, polarity);
+ if (ret)
+ err_msg = "Failed to register LED\n";
+
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
ret = skl_int3472_register_regulator(int3472, agpio);
@@ -355,21 +344,6 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
acpi_dev_free_resource_list(&resource_list);
- /*
- * If we find no clock enable GPIO pin then the privacy LED won't work.
- * We've never seen that situation, but it's possible. Warn the user so
- * it's clear what's happened.
- */
- if (int3472->clock.ena_gpio) {
- ret = skl_int3472_register_clock(int3472);
- if (ret)
- return ret;
- } else {
- if (int3472->clock.led_gpio)
- dev_warn(int3472->dev,
- "No clk GPIO. The privacy LED won't work\n");
- }
-
int3472->gpios.dev_id = int3472->sensor_name;
gpiod_add_lookup_table(&int3472->gpios);
@@ -386,8 +360,8 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev)
skl_int3472_unregister_clock(int3472);
gpiod_put(int3472->clock.ena_gpio);
- gpiod_put(int3472->clock.led_gpio);
+ skl_int3472_unregister_pled(int3472);
skl_int3472_unregister_regulator(int3472);
return 0;
diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c
new file mode 100644
index 0000000000000..251c6524458e7
--- /dev/null
+++ b/drivers/platform/x86/intel/int3472/led.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Hans de Goede <hdegoede@redhat.com> */
+
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/leds.h>
+#include "common.h"
+
+static int int3472_pled_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct int3472_discrete_device *int3472 =
+ container_of(led_cdev, struct int3472_discrete_device, pled.classdev);
+
+ gpiod_set_value_cansleep(int3472->pled.gpio, brightness);
+ return 0;
+}
+
+int skl_int3472_register_pled(struct int3472_discrete_device *int3472,
+ struct acpi_resource_gpio *agpio, u32 polarity)
+{
+ char *p, *path = agpio->resource_source.string_ptr;
+ int ret;
+
+ if (int3472->pled.classdev.dev)
+ return -EBUSY;
+
+ int3472->pled.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0],
+ "int3472,privacy-led");
+ if (IS_ERR(int3472->pled.gpio))
+ return dev_err_probe(int3472->dev, PTR_ERR(int3472->pled.gpio),
+ "getting privacy LED GPIO\n");
+
+ if (polarity == GPIO_ACTIVE_LOW)
+ gpiod_toggle_active_low(int3472->pled.gpio);
+
+ /* Ensure the pin is in output mode and non-active state */
+ gpiod_direction_output(int3472->pled.gpio, 0);
+
+ /* Generate the name, replacing the ':' in the ACPI devname with '_' */
+ snprintf(int3472->pled.name, sizeof(int3472->pled.name),
+ "%s::privacy_led", acpi_dev_name(int3472->sensor));
+ p = strchr(int3472->pled.name, ':');
+ *p = '_';
+
+ int3472->pled.classdev.name = int3472->pled.name;
+ int3472->pled.classdev.max_brightness = 1;
+ int3472->pled.classdev.brightness_set_blocking = int3472_pled_set;
+
+ ret = led_classdev_register(int3472->dev, &int3472->pled.classdev);
+ if (ret)
+ goto err_free_gpio;
+
+ int3472->pled.lookup.provider = int3472->pled.name;
+ int3472->pled.lookup.dev_id = int3472->sensor_name;
+ int3472->pled.lookup.con_id = "privacy-led";
+ led_add_lookup(&int3472->pled.lookup);
+
+ return 0;
+
+err_free_gpio:
+ gpiod_put(int3472->pled.gpio);
+ return ret;
+}
+
+void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472)
+{
+ if (IS_ERR_OR_NULL(int3472->pled.classdev.dev))
+ return;
+
+ led_remove_lookup(&int3472->pled.lookup);
+ led_classdev_unregister(&int3472->pled.classdev);
+ gpiod_put(int3472->pled.gpio);
+}
--
2.40.1
From 1de4d8e08e23fe60f05183972d6ddba03f7cb734 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 27 Jan 2023 21:37:28 +0100
Subject: [PATCH] platform/x86: int3472/discrete: Move GPIO request to
skl_int3472_register_clock()
Move the requesting of the clk-enable GPIO to skl_int3472_register_clock()
(and move the gpiod_put to unregister).
This mirrors the GPIO handling in skl_int3472_register_regulator() and
allows removing skl_int3472_map_gpio_to_clk() from discrete.c.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../x86/intel/int3472/clk_and_regulator.c | 28 +++++++++++++++--
drivers/platform/x86/intel/int3472/common.h | 3 +-
drivers/platform/x86/intel/int3472/discrete.c | 30 ++-----------------
3 files changed, 30 insertions(+), 31 deletions(-)
diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
index e3b597d933880..626e5e86f4e0c 100644
--- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c
+++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
@@ -86,18 +86,34 @@ static const struct clk_ops skl_int3472_clock_ops = {
.recalc_rate = skl_int3472_clk_recalc_rate,
};
-int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
+int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
+ struct acpi_resource_gpio *agpio)
{
+ char *path = agpio->resource_source.string_ptr;
struct clk_init_data init = {
.ops = &skl_int3472_clock_ops,
.flags = CLK_GET_RATE_NOCACHE,
};
int ret;
+ if (int3472->clock.cl)
+ return -EBUSY;
+
+ int3472->clock.ena_gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0],
+ "int3472,clk-enable");
+ if (IS_ERR(int3472->clock.ena_gpio))
+ return dev_err_probe(int3472->dev, PTR_ERR(int3472->clock.ena_gpio),
+ "getting clk-enable GPIO\n");
+
+ /* Ensure the pin is in output mode and non-active state */
+ gpiod_direction_output(int3472->clock.ena_gpio, 0);
+
init.name = kasprintf(GFP_KERNEL, "%s-clk",
acpi_dev_name(int3472->adev));
- if (!init.name)
- return -ENOMEM;
+ if (!init.name) {
+ ret = -ENOMEM;
+ goto out_put_gpio;
+ }
int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472);
@@ -123,14 +139,20 @@ int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
clk_unregister(int3472->clock.clk);
out_free_init_name:
kfree(init.name);
+out_put_gpio:
+ gpiod_put(int3472->clock.ena_gpio);
return ret;
}
void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)
{
+ if (!int3472->clock.cl)
+ return;
+
clkdev_drop(int3472->clock.cl);
clk_unregister(int3472->clock.clk);
+ gpiod_put(int3472->clock.ena_gpio);
}
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h
index 82dc37e08882e..0d4fa7d00b5f6 100644
--- a/drivers/platform/x86/intel/int3472/common.h
+++ b/drivers/platform/x86/intel/int3472/common.h
@@ -121,7 +121,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
struct acpi_device **sensor_adev_ret,
const char **name_ret);
-int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
+int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
+ struct acpi_resource_gpio *agpio);
void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 67d26f8a8d9a3..7bac0dd850c83 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -2,8 +2,6 @@
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
@@ -168,24 +166,6 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
return 0;
}
-static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
- struct acpi_resource_gpio *agpio)
-{
- char *path = agpio->resource_source.string_ptr;
- u16 pin = agpio->pin_table[0];
- struct gpio_desc *gpio;
-
- gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable");
- if (IS_ERR(gpio))
- return (PTR_ERR(gpio));
-
- int3472->clock.ena_gpio = gpio;
- /* Ensure the pin is in output mode and non-active state */
- gpiod_direction_output(int3472->clock.ena_gpio, 0);
-
- return skl_int3472_register_clock(int3472);
-}
-
static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)
{
switch (type) {
@@ -291,9 +271,9 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
- ret = skl_int3472_map_gpio_to_clk(int3472, agpio);
+ ret = skl_int3472_register_clock(int3472, agpio);
if (ret)
- err_msg = "Failed to map GPIO to clock\n";
+ err_msg = "Failed to register clock\n";
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
@@ -356,11 +336,7 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev)
gpiod_remove_lookup_table(&int3472->gpios);
- if (int3472->clock.cl)
- skl_int3472_unregister_clock(int3472);
-
- gpiod_put(int3472->clock.ena_gpio);
-
+ skl_int3472_unregister_clock(int3472);
skl_int3472_unregister_pled(int3472);
skl_int3472_unregister_regulator(int3472);
--
2.40.1
From bb8b9fcf26c5fa5cc18dd7c995e77b05ab3734db Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Fri, 27 Jan 2023 21:37:29 +0100
Subject: [PATCH] platform/x86: int3472/discrete: Get the polarity from the
_DSM entry
According to:
https://github.com/intel/ipu6-drivers/blob/master/patch/int3472-support-independent-clock-and-LED-gpios-5.17%2B.patch
Bits 31-24 of the _DSM pin entry integer value codes the active-value,
that is the actual physical signal (0 or 1) which needs to be output on
the pin to turn the sensor chip on (to make it active).
So if bits 31-24 are 0 for a reset pin, then the actual value of the reset
pin needs to be 0 to take the chip out of reset. IOW in this case the reset
signal is active-high rather then the default active-low.
And if bits 31-24 are 0 for a clk-en pin then the actual value of the clk
pin needs to be 0 to enable the clk. So in this case the clk-en signal
is active-low rather then the default active-high.
IOW if bits 31-24 are 0 for a pin, then the default polarity of the pin
is inverted.
Add a check for this and also propagate this new polarity to the clock
registration.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../platform/x86/intel/int3472/clk_and_regulator.c | 5 ++++-
drivers/platform/x86/intel/int3472/common.h | 2 +-
drivers/platform/x86/intel/int3472/discrete.c | 13 +++++++++++--
3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
index 626e5e86f4e0c..1086c3d834945 100644
--- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c
+++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
@@ -87,7 +87,7 @@ static const struct clk_ops skl_int3472_clock_ops = {
};
int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
- struct acpi_resource_gpio *agpio)
+ struct acpi_resource_gpio *agpio, u32 polarity)
{
char *path = agpio->resource_source.string_ptr;
struct clk_init_data init = {
@@ -105,6 +105,9 @@ int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
return dev_err_probe(int3472->dev, PTR_ERR(int3472->clock.ena_gpio),
"getting clk-enable GPIO\n");
+ if (polarity == GPIO_ACTIVE_LOW)
+ gpiod_toggle_active_low(int3472->clock.ena_gpio);
+
/* Ensure the pin is in output mode and non-active state */
gpiod_direction_output(int3472->clock.ena_gpio, 0);
diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h
index 0d4fa7d00b5f6..61688e450ce58 100644
--- a/drivers/platform/x86/intel/int3472/common.h
+++ b/drivers/platform/x86/intel/int3472/common.h
@@ -122,7 +122,7 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
const char **name_ret);
int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
- struct acpi_resource_gpio *agpio);
+ struct acpi_resource_gpio *agpio, u32 polarity);
void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 7bac0dd850c83..d402289ddaab8 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -234,11 +234,11 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
struct int3472_discrete_device *int3472 = data;
struct acpi_resource_gpio *agpio;
union acpi_object *obj;
+ u8 active_value, type;
const char *err_msg;
const char *func;
u32 polarity;
int ret;
- u8 type;
if (!acpi_gpio_get_io_resource(ares, &agpio))
return 1;
@@ -262,6 +262,15 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
int3472_get_func_and_polarity(type, &func, &polarity);
+ /* If bits 31-24 of the _DSM entry are all 0 then the signal is inverted */
+ active_value = obj->integer.value >> 24;
+ if (!active_value)
+ polarity ^= GPIO_ACTIVE_LOW;
+
+ dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func,
+ agpio->resource_source.string_ptr, agpio->pin_table[0],
+ (polarity == GPIO_ACTIVE_HIGH) ? "high" : "low");
+
switch (type) {
case INT3472_GPIO_TYPE_RESET:
case INT3472_GPIO_TYPE_POWERDOWN:
@@ -271,7 +280,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
- ret = skl_int3472_register_clock(int3472, agpio);
+ ret = skl_int3472_register_clock(int3472, agpio, polarity);
if (ret)
err_msg = "Failed to register clock\n";
--
2.40.1
From 8212950eb990fb351b637046020a536b1252b805 Mon Sep 17 00:00:00 2001
From: Kate Hsuan <hpa@redhat.com>
Date: Tue, 21 Mar 2023 23:37:16 +0800
Subject: [PATCH] platform: x86: int3472: Add MFD cell for tps68470 LED
Add MFD cell for tps68470-led.
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Signed-off-by: Kate Hsuan <hpa@redhat.com>
Patchset: cameras
---
drivers/platform/x86/intel/int3472/tps68470.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
index 6a0ff035cf209..2a7d01d3abc85 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -17,7 +17,7 @@
#define DESIGNED_FOR_CHROMEOS 1
#define DESIGNED_FOR_WINDOWS 2
-#define TPS68470_WIN_MFD_CELL_COUNT 3
+#define TPS68470_WIN_MFD_CELL_COUNT 4
static const struct mfd_cell tps68470_cros[] = {
{ .name = "tps68470-gpio" },
@@ -200,7 +200,8 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client)
cells[1].name = "tps68470-regulator";
cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata;
cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data);
- cells[2].name = "tps68470-gpio";
+ cells[2].name = "tps68470-led";
+ cells[3].name = "tps68470-gpio";
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
--
2.40.1
From bfccd551373e6a01dad37f48f28edb5b8add8215 Mon Sep 17 00:00:00 2001
From: Kate Hsuan <hpa@redhat.com>
Date: Tue, 21 Mar 2023 23:37:17 +0800
Subject: [PATCH] include: mfd: tps68470: Add masks for LEDA and LEDB
Add flags for both LEDA(TPS68470_ILEDCTL_ENA), LEDB
(TPS68470_ILEDCTL_ENB), and current control mask for LEDB
(TPS68470_ILEDCTL_CTRLB)
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Kate Hsuan <hpa@redhat.com>
Patchset: cameras
---
include/linux/mfd/tps68470.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h
index 7807fa329db00..2d2abb25b944f 100644
--- a/include/linux/mfd/tps68470.h
+++ b/include/linux/mfd/tps68470.h
@@ -34,6 +34,7 @@
#define TPS68470_REG_SGPO 0x22
#define TPS68470_REG_GPDI 0x26
#define TPS68470_REG_GPDO 0x27
+#define TPS68470_REG_ILEDCTL 0x28
#define TPS68470_REG_VCMVAL 0x3C
#define TPS68470_REG_VAUX1VAL 0x3D
#define TPS68470_REG_VAUX2VAL 0x3E
@@ -94,4 +95,8 @@
#define TPS68470_GPIO_MODE_OUT_CMOS 2
#define TPS68470_GPIO_MODE_OUT_ODRAIN 3
+#define TPS68470_ILEDCTL_ENA BIT(2)
+#define TPS68470_ILEDCTL_ENB BIT(6)
+#define TPS68470_ILEDCTL_CTRLB GENMASK(5, 4)
+
#endif /* __LINUX_MFD_TPS68470_H */
--
2.40.1
From 103b1cb0163ac1cc6507bfcb0b526a8538aee16b Mon Sep 17 00:00:00 2001
From: Kate Hsuan <hpa@redhat.com>
Date: Tue, 21 Mar 2023 23:37:18 +0800
Subject: [PATCH] leds: tps68470: Add LED control for tps68470
There are two LED controllers, LEDA indicator LED and LEDB flash LED for
tps68470. LEDA can be enabled by setting TPS68470_ILEDCTL_ENA. Moreover,
tps68470 provides four levels of power status for LEDB. If the
properties called "ti,ledb-current" can be found, the current will be
set according to the property values. These two LEDs can be controlled
through the LED class of sysfs (tps68470-leda and tps68470-ledb).
Signed-off-by: Kate Hsuan <hpa@redhat.com>
Patchset: cameras
---
drivers/leds/Kconfig | 12 +++
drivers/leds/Makefile | 1 +
drivers/leds/leds-tps68470.c | 185 +++++++++++++++++++++++++++++++++++
3 files changed, 198 insertions(+)
create mode 100644 drivers/leds/leds-tps68470.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 499d0f215a8bf..f0caddb6ab757 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -846,6 +846,18 @@ config LEDS_TPS6105X
It is a single boost converter primarily for white LEDs and
audio amplifiers.
+config LEDS_TPS68470
+ tristate "LED support for TI TPS68470"
+ depends on LEDS_CLASS
+ depends on INTEL_SKL_INT3472
+ help
+ This driver supports TPS68470 PMIC with LED chip.
+ It provides two LED controllers, with the ability to drive 2
+ indicator LEDs and 2 flash LEDs.
+
+ To compile this driver as a module, choose M and it will be
+ called leds-tps68470
+
config LEDS_IP30
tristate "LED support for SGI Octane machines"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4fd2f92cd1981..b381220400398 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o
obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o
obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o
+obj-$(CONFIG_LEDS_TPS68470) += leds-tps68470.o
obj-$(CONFIG_LEDS_TURRIS_OMNIA) += leds-turris-omnia.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
diff --git a/drivers/leds/leds-tps68470.c b/drivers/leds/leds-tps68470.c
new file mode 100644
index 0000000000000..35aeb5db89c8f
--- /dev/null
+++ b/drivers/leds/leds-tps68470.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LED driver for TPS68470 PMIC
+ *
+ * Copyright (C) 2023 Red Hat
+ *
+ * Authors:
+ * Kate Hsuan <hpa@redhat.com>
+ */
+
+#include <linux/leds.h>
+#include <linux/mfd/tps68470.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+
+#define lcdev_to_led(led_cdev) \
+ container_of(led_cdev, struct tps68470_led, lcdev)
+
+#define led_to_tps68470(led, index) \
+ container_of(led, struct tps68470_device, leds[index])
+
+enum tps68470_led_ids {
+ TPS68470_ILED_A,
+ TPS68470_ILED_B,
+ TPS68470_NUM_LEDS
+};
+
+static const char *tps68470_led_names[] = {
+ [TPS68470_ILED_A] = "tps68470-iled_a",
+ [TPS68470_ILED_B] = "tps68470-iled_b",
+};
+
+struct tps68470_led {
+ unsigned int led_id;
+ struct led_classdev lcdev;
+};
+
+struct tps68470_device {
+ struct device *dev;
+ struct regmap *regmap;
+ struct tps68470_led leds[TPS68470_NUM_LEDS];
+};
+
+enum ctrlb_current {
+ CTRLB_2MA = 0,
+ CTRLB_4MA = 1,
+ CTRLB_8MA = 2,
+ CTRLB_16MA = 3,
+};
+
+static int tps68470_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct tps68470_led *led = lcdev_to_led(led_cdev);
+ struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id);
+ struct regmap *regmap = tps68470->regmap;
+
+ switch (led->led_id) {
+ case TPS68470_ILED_A:
+ return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENA,
+ brightness ? TPS68470_ILEDCTL_ENA : 0);
+ case TPS68470_ILED_B:
+ return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENB,
+ brightness ? TPS68470_ILEDCTL_ENB : 0);
+ }
+ return -EINVAL;
+}
+
+static enum led_brightness tps68470_brightness_get(struct led_classdev *led_cdev)
+{
+ struct tps68470_led *led = lcdev_to_led(led_cdev);
+ struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id);
+ struct regmap *regmap = tps68470->regmap;
+ int ret = 0;
+ int value = 0;
+
+ ret = regmap_read(regmap, TPS68470_REG_ILEDCTL, &value);
+ if (ret)
+ return dev_err_probe(led_cdev->dev, -EINVAL, "failed on reading register\n");
+
+ switch (led->led_id) {
+ case TPS68470_ILED_A:
+ value = value & TPS68470_ILEDCTL_ENA;
+ break;
+ case TPS68470_ILED_B:
+ value = value & TPS68470_ILEDCTL_ENB;
+ break;
+ }
+
+ return value ? LED_ON : LED_OFF;
+}
+
+
+static int tps68470_ledb_current_init(struct platform_device *pdev,
+ struct tps68470_device *tps68470)
+{
+ int ret = 0;
+ unsigned int curr;
+
+ /* configure LEDB current if the properties can be got */
+ if (!device_property_read_u32(&pdev->dev, "ti,ledb-current", &curr)) {
+ if (curr > CTRLB_16MA) {
+ dev_err(&pdev->dev,
+ "Invalid LEDB current value: %d\n",
+ curr);
+ return -EINVAL;
+ }
+ ret = regmap_update_bits(tps68470->regmap, TPS68470_REG_ILEDCTL,
+ TPS68470_ILEDCTL_CTRLB, curr);
+ }
+ return ret;
+}
+
+static int tps68470_leds_probe(struct platform_device *pdev)
+{
+ int i = 0;
+ int ret = 0;
+ struct tps68470_device *tps68470;
+ struct tps68470_led *led;
+ struct led_classdev *lcdev;
+
+ tps68470 = devm_kzalloc(&pdev->dev, sizeof(struct tps68470_device),
+ GFP_KERNEL);
+ if (!tps68470)
+ return -ENOMEM;
+
+ tps68470->dev = &pdev->dev;
+ tps68470->regmap = dev_get_drvdata(pdev->dev.parent);
+
+ for (i = 0; i < TPS68470_NUM_LEDS; i++) {
+ led = &tps68470->leds[i];
+ lcdev = &led->lcdev;
+
+ led->led_id = i;
+
+ lcdev->name = devm_kasprintf(tps68470->dev, GFP_KERNEL, "%s::%s",
+ tps68470_led_names[i], LED_FUNCTION_INDICATOR);
+ if (!lcdev->name)
+ return -ENOMEM;
+
+ lcdev->max_brightness = 1;
+ lcdev->brightness = 0;
+ lcdev->brightness_set_blocking = tps68470_brightness_set;
+ lcdev->brightness_get = tps68470_brightness_get;
+ lcdev->dev = &pdev->dev;
+
+ ret = devm_led_classdev_register(tps68470->dev, lcdev);
+ if (ret) {
+ dev_err_probe(tps68470->dev, ret,
+ "error registering led\n");
+ goto err_exit;
+ }
+
+ if (i == TPS68470_ILED_B) {
+ ret = tps68470_ledb_current_init(pdev, tps68470);
+ if (ret)
+ goto err_exit;
+ }
+ }
+
+err_exit:
+ if (ret) {
+ for (i = 0; i < TPS68470_NUM_LEDS; i++) {
+ if (tps68470->leds[i].lcdev.name)
+ devm_led_classdev_unregister(&pdev->dev,
+ &tps68470->leds[i].lcdev);
+ }
+ }
+
+ return ret;
+}
+static struct platform_driver tps68470_led_driver = {
+ .driver = {
+ .name = "tps68470-led",
+ },
+ .probe = tps68470_leds_probe,
+};
+
+module_platform_driver(tps68470_led_driver);
+
+MODULE_ALIAS("platform:tps68470-led");
+MODULE_DESCRIPTION("LED driver for TPS68470 PMIC");
+MODULE_LICENSE("GPL v2");
--
2.40.1
From 2fbc466ea2a12f1c36f613b4429ffc63723e579a Mon Sep 17 00:00:00 2001
From: Daniel Scally <dan.scally@ideasonboard.com>
Date: Wed, 22 Mar 2023 11:01:42 +0000
Subject: [PATCH] media: v4l2-core: Acquire privacy led in
v4l2_async_register_subdev()
The current call to v4l2_subdev_get_privacy_led() is contained in
v4l2_async_register_subdev_sensor(), but that function isn't used by
all the sensor drivers. Move the acquisition of the privacy led to
v4l2_async_register_subdev() instead.
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Patchset: cameras
---
drivers/media/v4l2-core/v4l2-async.c | 4 ++++
drivers/media/v4l2-core/v4l2-fwnode.c | 4 ----
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index b26dcb8d423e1..43774d5acf5d1 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -757,6 +757,10 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
struct v4l2_async_notifier *notifier;
int ret;
+ ret = v4l2_subdev_get_privacy_led(sd);
+ if (ret < 0)
+ return ret;
+
/*
* No reference taken. The reference is held by the device
* (struct v4l2_subdev.dev), and async sub-device does not
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 8501b6931d3a0..077d85553b2b9 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -1303,10 +1303,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_init(notifier);
- ret = v4l2_subdev_get_privacy_led(sd);
- if (ret < 0)
- goto out_cleanup;
-
ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
if (ret < 0)
goto out_cleanup;
--
2.40.1
From b0f6abba0b800c844aca94a90fa43dde3f6f601c Mon Sep 17 00:00:00 2001
From: Daniel Scally <dan.scally@ideasonboard.com>
Date: Tue, 21 Mar 2023 13:45:26 +0000
Subject: [PATCH] media: i2c: Clarify that gain is Analogue gain in OV7251
Update the control ID for the gain control in the ov7251 driver to
V4L2_CID_ANALOGUE_GAIN.
Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Patchset: cameras
---
drivers/media/i2c/ov7251.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
index 88e9874352853..ff7b2c26da835 100644
--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -1051,7 +1051,7 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_EXPOSURE:
ret = ov7251_set_exposure(ov7251, ctrl->val);
break;
- case V4L2_CID_GAIN:
+ case V4L2_CID_ANALOGUE_GAIN:
ret = ov7251_set_gain(ov7251, ctrl->val);
break;
case V4L2_CID_TEST_PATTERN:
@@ -1551,7 +1551,7 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251)
ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 32, 1, 32);
ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
- V4L2_CID_GAIN, 16, 1023, 1, 16);
+ V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16);
v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops,
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
--
2.40.1