linux-surface/patches/5.13/0009-cameras.patch
Maximilian Luz f69eefacf7
Update v5.13 patches
Changes:
 - Rebase onto v5.13.13

Links:
 - kernel: e6d532e204
2021-09-03 00:07:13 +02:00

5714 lines
176 KiB
Diff

From 2a96c94a25ca756a6056d6c89e492cc2b671bbd4 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 3 Jun 2021 23:40:02 +0100
Subject: [PATCH] ACPI: scan: Extend acpi_walk_dep_device_list()
The acpi_walk_dep_device_list() function is not as generic as its
name implies, serving only to decrement the dependency count for each
dependent device of the input.
Extend it to accept a callback which can be applied to all the
dependencies in acpi_dep_list.
Replace all existing calls to the function with calls to a wrapper,
passing a callback that applies the same dependency reduction.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Maximilian Luz <luzmaximilian@gmail.com> # for platform/surface parts
Signed-off-by: Daniel Scally <djrscally@gmail.com>
[ rjw: Changelog edits ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Patchset: cameras
---
drivers/acpi/ec.c | 2 +-
drivers/acpi/pmic/intel_pmic_chtdc_ti.c | 2 +-
drivers/acpi/scan.c | 69 ++++++++++++++-----
drivers/gpio/gpiolib-acpi.c | 10 +--
drivers/i2c/i2c-core-acpi.c | 8 +--
drivers/platform/surface/aggregator/core.c | 6 +-
drivers/platform/surface/surface3_power.c | 22 +++---
.../platform/surface/surface_acpi_notify.c | 7 +-
include/acpi/acpi_bus.h | 7 ++
include/linux/acpi.h | 4 +-
10 files changed, 90 insertions(+), 47 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 87c3b4a099b9..e629e891d1bb 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1629,7 +1629,7 @@ static int acpi_ec_add(struct acpi_device *device)
WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);
/* Reprobe devices depending on the EC */
- acpi_walk_dep_device_list(ec->handle);
+ acpi_dev_clear_dependencies(device);
acpi_handle_debug(ec->handle, "enumerated.\n");
return 0;
diff --git a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
index a5101b07611a..fef7831d0d63 100644
--- a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
+++ b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c
@@ -117,7 +117,7 @@ static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev)
return err;
/* Re-enumerate devices depending on PMIC */
- acpi_walk_dep_device_list(ACPI_HANDLE(pdev->dev.parent));
+ acpi_dev_clear_dependencies(ACPI_COMPANION(pdev->dev.parent));
return 0;
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 438df8da6d12..607aeea9a210 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -47,12 +47,6 @@ static DEFINE_MUTEX(acpi_hp_context_lock);
*/
static u64 spcr_uart_addr;
-struct acpi_dep_data {
- struct list_head node;
- acpi_handle supplier;
- acpi_handle consumer;
-};
-
void acpi_scan_lock_acquire(void)
{
mutex_lock(&acpi_scan_lock);
@@ -2107,30 +2101,69 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
device->handler->hotplug.notify_online(device);
}
-void acpi_walk_dep_device_list(acpi_handle handle)
+static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
{
- struct acpi_dep_data *dep, *tmp;
struct acpi_device *adev;
+ acpi_bus_get_device(dep->consumer, &adev);
+
+ if (adev) {
+ adev->dep_unmet--;
+ if (!adev->dep_unmet)
+ acpi_bus_attach(adev, true);
+ }
+
+ list_del(&dep->node);
+ kfree(dep);
+
+ return 0;
+}
+
+/**
+ * acpi_walk_dep_device_list - Apply a callback to every entry in acpi_dep_list
+ * @handle: The ACPI handle of the supplier device
+ * @callback: Pointer to the callback function to apply
+ * @data: Pointer to some data to pass to the callback
+ *
+ * The return value of the callback determines this function's behaviour. If 0
+ * is returned we continue to iterate over acpi_dep_list. If a positive value
+ * is returned then the loop is broken but this function returns 0. If a
+ * negative value is returned by the callback then the loop is broken and that
+ * value is returned as the final error.
+ */
+int acpi_walk_dep_device_list(acpi_handle handle,
+ int (*callback)(struct acpi_dep_data *, void *),
+ void *data)
+{
+ struct acpi_dep_data *dep, *tmp;
+ int ret;
+
mutex_lock(&acpi_dep_list_lock);
list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) {
if (dep->supplier == handle) {
- acpi_bus_get_device(dep->consumer, &adev);
-
- if (adev) {
- adev->dep_unmet--;
- if (!adev->dep_unmet)
- acpi_bus_attach(adev, true);
- }
-
- list_del(&dep->node);
- kfree(dep);
+ ret = callback(dep, data);
+ if (ret)
+ break;
}
}
mutex_unlock(&acpi_dep_list_lock);
+
+ return ret > 0 ? 0 : ret;
}
EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list);
+/**
+ * acpi_dev_clear_dependencies - Inform consumers that the device is now active
+ * @supplier: Pointer to the supplier &struct acpi_device
+ *
+ * Clear dependencies on the given device.
+ */
+void acpi_dev_clear_dependencies(struct acpi_device *supplier)
+{
+ acpi_walk_dep_device_list(supplier->handle, acpi_scan_clear_dep, NULL);
+}
+EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies);
+
/**
* acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
* @handle: Root of the namespace scope to scan.
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 3ef22a3c104d..5b4111e4be3f 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -1233,14 +1233,14 @@ static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip)
void acpi_gpiochip_add(struct gpio_chip *chip)
{
struct acpi_gpio_chip *acpi_gpio;
- acpi_handle handle;
+ struct acpi_device *adev;
acpi_status status;
if (!chip || !chip->parent)
return;
- handle = ACPI_HANDLE(chip->parent);
- if (!handle)
+ adev = ACPI_COMPANION(chip->parent);
+ if (!adev)
return;
acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL);
@@ -1254,7 +1254,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
INIT_LIST_HEAD(&acpi_gpio->events);
INIT_LIST_HEAD(&acpi_gpio->deferred_req_irqs_list_entry);
- status = acpi_attach_data(handle, acpi_gpio_chip_dh, acpi_gpio);
+ status = acpi_attach_data(adev->handle, acpi_gpio_chip_dh, acpi_gpio);
if (ACPI_FAILURE(status)) {
dev_err(chip->parent, "Failed to attach ACPI GPIO chip\n");
kfree(acpi_gpio);
@@ -1263,7 +1263,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
acpi_gpiochip_request_regions(acpi_gpio);
acpi_gpiochip_scan_gpios(acpi_gpio);
- acpi_walk_dep_device_list(handle);
+ acpi_dev_clear_dependencies(adev);
}
void acpi_gpiochip_remove(struct gpio_chip *chip)
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index deceed0d76c6..13eb5ac82729 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -259,8 +259,8 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
*/
void i2c_acpi_register_devices(struct i2c_adapter *adap)
{
+ struct acpi_device *adev;
acpi_status status;
- acpi_handle handle;
if (!has_acpi_companion(&adap->dev))
return;
@@ -275,11 +275,11 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap)
if (!adap->dev.parent)
return;
- handle = ACPI_HANDLE(adap->dev.parent);
- if (!handle)
+ adev = ACPI_COMPANION(adap->dev.parent);
+ if (!adev)
return;
- acpi_walk_dep_device_list(handle);
+ acpi_dev_clear_dependencies(adev);
}
static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = {
diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c
index 5d780e55f4a1..279d9df19c01 100644
--- a/drivers/platform/surface/aggregator/core.c
+++ b/drivers/platform/surface/aggregator/core.c
@@ -621,8 +621,8 @@ static const struct acpi_gpio_mapping ssam_acpi_gpios[] = {
static int ssam_serial_hub_probe(struct serdev_device *serdev)
{
+ struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev);
struct ssam_controller *ctrl;
- acpi_handle *ssh = ACPI_HANDLE(&serdev->dev);
acpi_status astatus;
int status;
@@ -652,7 +652,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
if (status)
goto err_devopen;
- astatus = ssam_serdev_setup_via_acpi(ssh, serdev);
+ astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
if (ACPI_FAILURE(astatus)) {
status = -ENXIO;
goto err_devinit;
@@ -706,7 +706,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
* For now let's thus default power/wakeup to false.
*/
device_set_wakeup_capable(&serdev->dev, true);
- acpi_walk_dep_device_list(ssh);
+ acpi_dev_clear_dependencies(ssh);
return 0;
diff --git a/drivers/platform/surface/surface3_power.c b/drivers/platform/surface/surface3_power.c
index cc4f9cba6856..dea82aa1abd4 100644
--- a/drivers/platform/surface/surface3_power.c
+++ b/drivers/platform/surface/surface3_power.c
@@ -446,12 +446,12 @@ mshw0011_space_handler(u32 function, acpi_physical_address command,
static int mshw0011_install_space_handler(struct i2c_client *client)
{
- acpi_handle handle;
+ struct acpi_device *adev;
struct mshw0011_handler_data *data;
acpi_status status;
- handle = ACPI_HANDLE(&client->dev);
- if (!handle)
+ adev = ACPI_COMPANION(&client->dev);
+ if (!adev)
return -ENODEV;
data = kzalloc(sizeof(struct mshw0011_handler_data),
@@ -460,25 +460,25 @@ static int mshw0011_install_space_handler(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- status = acpi_bus_attach_private_data(handle, (void *)data);
+ status = acpi_bus_attach_private_data(adev->handle, (void *)data);
if (ACPI_FAILURE(status)) {
kfree(data);
return -ENOMEM;
}
- status = acpi_install_address_space_handler(handle,
- ACPI_ADR_SPACE_GSBUS,
- &mshw0011_space_handler,
- NULL,
- data);
+ status = acpi_install_address_space_handler(adev->handle,
+ ACPI_ADR_SPACE_GSBUS,
+ &mshw0011_space_handler,
+ NULL,
+ data);
if (ACPI_FAILURE(status)) {
dev_err(&client->dev, "Error installing i2c space handler\n");
- acpi_bus_detach_private_data(handle);
+ acpi_bus_detach_private_data(adev->handle);
kfree(data);
return -ENOMEM;
}
- acpi_walk_dep_device_list(handle);
+ acpi_dev_clear_dependencies(adev);
return 0;
}
diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c
index ef9c1f8e8336..8339988d95c1 100644
--- a/drivers/platform/surface/surface_acpi_notify.c
+++ b/drivers/platform/surface/surface_acpi_notify.c
@@ -798,7 +798,7 @@ static int san_consumer_links_setup(struct platform_device *pdev)
static int san_probe(struct platform_device *pdev)
{
- acpi_handle san = ACPI_HANDLE(&pdev->dev);
+ struct acpi_device *san = ACPI_COMPANION(&pdev->dev);
struct ssam_controller *ctrl;
struct san_data *data;
acpi_status astatus;
@@ -821,7 +821,8 @@ static int san_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- astatus = acpi_install_address_space_handler(san, ACPI_ADR_SPACE_GSBUS,
+ astatus = acpi_install_address_space_handler(san->handle,
+ ACPI_ADR_SPACE_GSBUS,
&san_opreg_handler, NULL,
&data->info);
if (ACPI_FAILURE(astatus))
@@ -835,7 +836,7 @@ static int san_probe(struct platform_device *pdev)
if (status)
goto err_install_dev;
- acpi_walk_dep_device_list(san);
+ acpi_dev_clear_dependencies(san);
return 0;
err_install_dev:
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 394eb42d0ec1..b934c77b735f 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -280,6 +280,12 @@ struct acpi_device_power {
struct acpi_device_power_state states[ACPI_D_STATE_COUNT]; /* Power states (D0-D3Cold) */
};
+struct acpi_dep_data {
+ struct list_head node;
+ acpi_handle supplier;
+ acpi_handle consumer;
+};
+
/* Performance Management */
struct acpi_device_perf_flags {
@@ -685,6 +691,7 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2);
+void acpi_dev_clear_dependencies(struct acpi_device *supplier);
struct acpi_device *
acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv);
struct acpi_device *
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index c60745f657e9..170b9bebdb2b 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -666,7 +666,9 @@ extern bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv);
int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
int acpi_device_modalias(struct device *, char *, int);
-void acpi_walk_dep_device_list(acpi_handle handle);
+int acpi_walk_dep_device_list(acpi_handle handle,
+ int (*callback)(struct acpi_dep_data *, void *),
+ void *data);
struct platform_device *acpi_create_platform_device(struct acpi_device *,
struct property_entry *);
--
2.33.0
From 49022760499bae8d2971805281842cf844271b05 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 3 Jun 2021 23:40:03 +0100
Subject: [PATCH] ACPI: scan: Add function to fetch dependent of ACPI device
In some ACPI tables we encounter, devices use the _DEP method to assert
a dependence on other ACPI devices as opposed to the OpRegions that the
specification intends.
We need to be able to find those devices "from" the dependee, so add
a callback and a wrapper to walk over the acpi_dep_list and return
the dependent ACPI device.
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Patchset: cameras
---
drivers/acpi/scan.c | 35 +++++++++++++++++++++++++++++++++++
include/acpi/acpi_bus.h | 1 +
2 files changed, 36 insertions(+)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 607aeea9a210..7f36295579ce 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2101,6 +2101,20 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
device->handler->hotplug.notify_online(device);
}
+static int acpi_dev_get_first_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
+{
+ struct acpi_device *adev;
+
+ adev = acpi_bus_get_acpi_device(dep->consumer);
+ if (!adev)
+ /* If we don't find an adev then we want to continue parsing */
+ return 0;
+
+ *(struct acpi_device **)data = adev;
+
+ return 1;
+}
+
static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
{
struct acpi_device *adev;
@@ -2164,6 +2178,27 @@ void acpi_dev_clear_dependencies(struct acpi_device *supplier)
}
EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies);
+/**
+ * acpi_dev_get_first_consumer_dev - Return ACPI device dependent on @supplier
+ * @supplier: Pointer to the dependee device
+ *
+ * Returns the first &struct acpi_device which declares itself dependent on
+ * @supplier via the _DEP buffer, parsed from the acpi_dep_list.
+ *
+ * The caller is responsible for putting the reference to adev when it is no
+ * longer needed.
+ */
+struct acpi_device *acpi_dev_get_first_consumer_dev(struct acpi_device *supplier)
+{
+ struct acpi_device *adev = NULL;
+
+ acpi_walk_dep_device_list(supplier->handle,
+ acpi_dev_get_first_consumer_dev_cb, &adev);
+
+ return adev;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_first_consumer_dev);
+
/**
* acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
* @handle: Root of the namespace scope to scan.
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index b934c77b735f..db20595b24d1 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -692,6 +692,7 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2);
void acpi_dev_clear_dependencies(struct acpi_device *supplier);
+struct acpi_device *acpi_dev_get_first_consumer_dev(struct acpi_device *supplier);
struct acpi_device *
acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv);
struct acpi_device *
--
2.33.0
From b59579c09cf15ff8c78c50d3f4a457bb198d7906 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 3 Jun 2021 23:40:04 +0100
Subject: [PATCH] gpiolib: acpi: Introduce acpi_get_and_request_gpiod() helper
We need to be able to translate GPIO resources in an ACPI device's _CRS
into GPIO descriptor array. Those are represented in _CRS as a pathname
to a GPIO device plus the pin's index number: the acpi_get_gpiod()
function is perfect for that purpose.
As it's currently only used internally within the GPIO layer, provide and
export a wrapper function that additionally holds a reference to the GPIO
device.
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Patchset: cameras
---
drivers/gpio/gpiolib-acpi.c | 28 ++++++++++++++++++++++++++++
include/linux/gpio/consumer.h | 2 ++
2 files changed, 30 insertions(+)
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 5b4111e4be3f..b5acb2c50836 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -128,6 +128,34 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
return gpiochip_get_desc(chip, pin);
}
+/**
+ * acpi_get_and_request_gpiod - Translate ACPI GPIO pin to GPIO descriptor and
+ * hold a refcount to the GPIO device.
+ * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
+ * @pin: ACPI GPIO pin number (0-based, controller-relative)
+ * @label: Label to pass to gpiod_request()
+ *
+ * This function is a simple pass-through to acpi_get_gpiod(), except that
+ * as it is intended for use outside of the GPIO layer (in a similar fashion to
+ * gpiod_get_index() for example) it also holds a reference to the GPIO device.
+ */
+struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ gpio = acpi_get_gpiod(path, pin);
+ if (IS_ERR(gpio))
+ return gpio;
+
+ ret = gpiod_request(gpio, label);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return gpio;
+}
+EXPORT_SYMBOL_GPL(acpi_get_and_request_gpiod);
+
static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
{
struct acpi_gpio_event *event = data;
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index c73b25bc9213..566feb56601f 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -692,6 +692,8 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev,
const struct acpi_gpio_mapping *gpios);
void devm_acpi_dev_remove_driver_gpios(struct device *dev);
+struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label);
+
#else /* CONFIG_GPIOLIB && CONFIG_ACPI */
struct acpi_device;
--
2.33.0
From 6e23f9976c758315c57f0b8a52d002507d087d88 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 3 Jun 2021 23:40:05 +0100
Subject: [PATCH] gpiolib: acpi: Add acpi_gpio_get_io_resource()
Add a function to verify that a given ACPI resource represents a GpioIo()
type of resource, and return it if so.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Patchset: cameras
---
drivers/gpio/gpiolib-acpi.c | 23 +++++++++++++++++++++++
include/linux/acpi.h | 7 +++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index b5acb2c50836..411525ac4cc4 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -196,6 +196,29 @@ bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
}
EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);
+/**
+ * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO
+ * I/O resource or return False if not.
+ * @ares: Pointer to the ACPI resource to fetch
+ * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer
+ */
+bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
+ struct acpi_resource_gpio **agpio)
+{
+ struct acpi_resource_gpio *gpio;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
+ return false;
+
+ gpio = &ares->data.gpio;
+ if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO)
+ return false;
+
+ *agpio = gpio;
+ return true;
+}
+EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource);
+
static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
struct acpi_gpio_event *event)
{
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 170b9bebdb2b..e8ba7063c000 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1098,6 +1098,8 @@ void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const c
#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
struct acpi_resource_gpio **agpio);
+bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
+ struct acpi_resource_gpio **agpio);
int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index);
#else
static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
@@ -1105,6 +1107,11 @@ static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
{
return false;
}
+static inline bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
+ struct acpi_resource_gpio **agpio)
+{
+ return false;
+}
static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev,
const char *name, int index)
{
--
2.33.0
From e5553a8ae0cb56f7a447e369eced872c6cb9e4d2 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 3 Jun 2021 23:40:06 +0100
Subject: [PATCH] platform/x86: Add intel_skl_int3472 driver
ACPI devices with _HID INT3472 are currently matched to the tps68470
driver, however this does not cover all situations in which that _HID
occurs. We've encountered three possibilities:
1. On Chrome OS devices, an ACPI device with _HID INT3472 (representing
a physical TPS68470 device) that requires a GPIO and OpRegion driver
2. On devices designed for Windows, an ACPI device with _HID INT3472
(again representing a physical TPS68470 device) which requires GPIO,
Clock and Regulator drivers.
3. On other devices designed for Windows, an ACPI device with _HID
INT3472 which does **not** represent a physical TPS68470, and is instead
used as a dummy device to group some system GPIO lines which are meant
to be consumed by the sensor that is dependent on this entry.
This commit adds a new module, registering a platform driver to deal
with the 3rd scenario plus an i2c driver to deal with #1 and #2, by
querying the CLDB buffer found against INT3472 entries to determine
which is most appropriate.
Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/20210603224007.120560-6-djrscally@gmail.com
[hdegoede@redhat.com Make skl_int3472_tps68470_calc_type() static]
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
MAINTAINERS | 5 +
drivers/platform/x86/Kconfig | 2 +
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/intel-int3472/Kconfig | 30 ++
drivers/platform/x86/intel-int3472/Makefile | 5 +
.../intel_skl_int3472_clk_and_regulator.c | 196 ++++++++
.../intel-int3472/intel_skl_int3472_common.c | 106 +++++
.../intel-int3472/intel_skl_int3472_common.h | 118 +++++
.../intel_skl_int3472_discrete.c | 417 ++++++++++++++++++
.../intel_skl_int3472_tps68470.c | 137 ++++++
10 files changed, 1017 insertions(+)
create mode 100644 drivers/platform/x86/intel-int3472/Kconfig
create mode 100644 drivers/platform/x86/intel-int3472/Makefile
create mode 100644 drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
create mode 100644 drivers/platform/x86/intel-int3472/intel_skl_int3472_common.c
create mode 100644 drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
create mode 100644 drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
create mode 100644 drivers/platform/x86/intel-int3472/intel_skl_int3472_tps68470.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0cce91cd5624..1db7311d78a6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9386,6 +9386,11 @@ S: Maintained
F: arch/x86/include/asm/intel_scu_ipc.h
F: drivers/platform/x86/intel_scu_*
+INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
+M: Daniel Scally <djrscally@gmail.com>
+S: Maintained
+F: drivers/platform/x86/intel-int3472/
+
INTEL SPEED SELECT TECHNOLOGY
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 60592fb88e7a..88134aaacefc 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -697,6 +697,8 @@ config INTEL_CHT_INT33FE
device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m
for Type-C device.
+source "drivers/platform/x86/intel-int3472/Kconfig"
+
config INTEL_HID_EVENT
tristate "INTEL HID Event"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index dcc8cdb95b4d..c0612c02d037 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
+obj-$(CONFIG_INTEL_SKL_INT3472) += intel-int3472/
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
# MSI
diff --git a/drivers/platform/x86/intel-int3472/Kconfig b/drivers/platform/x86/intel-int3472/Kconfig
new file mode 100644
index 000000000000..c112878e833b
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/Kconfig
@@ -0,0 +1,30 @@
+config INTEL_SKL_INT3472
+ tristate "Intel SkyLake ACPI INT3472 Driver"
+ depends on ACPI
+ depends on COMMON_CLK && CLKDEV_LOOKUP
+ depends on I2C
+ depends on GPIOLIB
+ depends on REGULATOR
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ This driver adds power controller support for the Intel SkyCam
+ devices found on the Intel SkyLake platforms.
+
+ The INT3472 is a camera power controller, a logical device found on
+ Intel Skylake-based systems that can map to different hardware
+ devices depending on the platform. On machines designed for Chrome OS
+ it maps to a TPS68470 camera PMIC. On machines designed for Windows,
+ it maps to either a TP68470 camera PMIC, a uP6641Q sensor PMIC, or a
+ set of discrete GPIOs and power gates.
+
+ If your device was designed for Chrome OS, this driver will provide
+ an ACPI OpRegion, which must be available before any of the devices
+ using it are probed. For this reason, you should select Y if your
+ device was designed for ChromeOS. For the same reason the
+ I2C_DESIGNWARE_PLATFORM option must be set to Y too.
+
+ Say Y or M here if you have a SkyLake device designed for use
+ with Windows or ChromeOS. Say N here if you are not sure.
+
+ The module will be named "intel-skl-int3472".
diff --git a/drivers/platform/x86/intel-int3472/Makefile b/drivers/platform/x86/intel-int3472/Makefile
new file mode 100644
index 000000000000..48bd97f0a04e
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472.o
+intel_skl_int3472-objs := intel_skl_int3472_common.o \
+ intel_skl_int3472_discrete.o \
+ intel_skl_int3472_tps68470.o \
+ intel_skl_int3472_clk_and_regulator.o
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
new file mode 100644
index 000000000000..ceee860e2c07
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/* 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/regulator/driver.h>
+#include <linux/slab.h>
+
+#include "intel_skl_int3472_common.h"
+
+/*
+ * The regulators have to have .ops to be valid, but the only ops we actually
+ * support are .enable and .disable which are handled via .ena_gpiod. Pass an
+ * empty struct to clear the check without lying about capabilities.
+ */
+static const struct regulator_ops int3472_gpio_regulator_ops;
+
+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;
+}
+
+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)
+{
+ /*
+ * We're just turning a GPIO on to enable the clock, which operation
+ * has the potential to sleep. Given .enable() cannot sleep, but
+ * .prepare() can, we toggle the GPIO in .prepare() instead. Thus,
+ * nothing to do here.
+ */
+ return 0;
+}
+
+static void skl_int3472_clk_disable(struct clk_hw *hw)
+{
+ /* Likewise, nothing to do here... */
+}
+
+static unsigned int skl_int3472_get_clk_frequency(struct int3472_discrete_device *int3472)
+{
+ union acpi_object *obj;
+ unsigned int freq;
+
+ obj = skl_int3472_get_acpi_buffer(int3472->sensor, "SSDB");
+ if (IS_ERR(obj))
+ return 0; /* report rate as 0 on error */
+
+ if (obj->buffer.length < CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET + sizeof(u32)) {
+ dev_err(int3472->dev, "The buffer is too small\n");
+ kfree(obj);
+ return 0;
+ }
+
+ freq = *(u32 *)(obj->buffer.pointer + CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET);
+
+ kfree(obj);
+ return freq;
+}
+
+static unsigned long skl_int3472_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct int3472_gpio_clock *clk = to_int3472_clk(hw);
+
+ return clk->frequency;
+}
+
+static const struct clk_ops skl_int3472_clock_ops = {
+ .prepare = skl_int3472_clk_prepare,
+ .unprepare = skl_int3472_clk_unprepare,
+ .enable = skl_int3472_clk_enable,
+ .disable = skl_int3472_clk_disable,
+ .recalc_rate = skl_int3472_clk_recalc_rate,
+};
+
+int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
+{
+ struct clk_init_data init = {
+ .ops = &skl_int3472_clock_ops,
+ .flags = CLK_GET_RATE_NOCACHE,
+ };
+ int ret;
+
+ init.name = kasprintf(GFP_KERNEL, "%s-clk",
+ acpi_dev_name(int3472->adev));
+ if (!init.name)
+ return -ENOMEM;
+
+ int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472);
+
+ int3472->clock.clk_hw.init = &init;
+ int3472->clock.clk = clk_register(&int3472->adev->dev,
+ &int3472->clock.clk_hw);
+ if (IS_ERR(int3472->clock.clk)) {
+ ret = PTR_ERR(int3472->clock.clk);
+ goto out_free_init_name;
+ }
+
+ int3472->clock.cl = clkdev_create(int3472->clock.clk, NULL,
+ int3472->sensor_name);
+ if (!int3472->clock.cl) {
+ ret = -ENOMEM;
+ goto err_unregister_clk;
+ }
+
+ kfree(init.name);
+ return 0;
+
+err_unregister_clk:
+ clk_unregister(int3472->clock.clk);
+out_free_init_name:
+ kfree(init.name);
+
+ return ret;
+}
+
+int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
+ struct acpi_resource *ares)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ const struct int3472_sensor_config *sensor_config;
+ struct regulator_consumer_supply supply_map;
+ struct regulator_init_data init_data = { };
+ struct regulator_config cfg = { };
+ int ret;
+
+ sensor_config = int3472->sensor_config;
+ if (IS_ERR(sensor_config)) {
+ dev_err(int3472->dev, "No sensor module config\n");
+ return PTR_ERR(sensor_config);
+ }
+
+ if (!sensor_config->supply_map.supply) {
+ dev_err(int3472->dev, "No supply name defined\n");
+ return -ENODEV;
+ }
+
+ init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
+ init_data.num_consumer_supplies = 1;
+ supply_map = sensor_config->supply_map;
+ supply_map.dev_name = int3472->sensor_name;
+ init_data.consumer_supplies = &supply_map;
+
+ snprintf(int3472->regulator.regulator_name,
+ sizeof(int3472->regulator.regulator_name), "%s-regulator",
+ acpi_dev_name(int3472->adev));
+ snprintf(int3472->regulator.supply_name,
+ GPIO_REGULATOR_SUPPLY_NAME_LENGTH, "supply-0");
+
+ int3472->regulator.rdesc = INT3472_REGULATOR(
+ int3472->regulator.regulator_name,
+ int3472->regulator.supply_name,
+ &int3472_gpio_regulator_ops);
+
+ int3472->regulator.gpio = acpi_get_and_request_gpiod(path,
+ ares->data.gpio.pin_table[0],
+ "int3472,regulator");
+ if (IS_ERR(int3472->regulator.gpio)) {
+ dev_err(int3472->dev, "Failed to get regulator GPIO line\n");
+ return PTR_ERR(int3472->regulator.gpio);
+ }
+
+ cfg.dev = &int3472->adev->dev;
+ cfg.init_data = &init_data;
+ cfg.ena_gpiod = int3472->regulator.gpio;
+
+ int3472->regulator.rdev = regulator_register(&int3472->regulator.rdesc,
+ &cfg);
+ if (IS_ERR(int3472->regulator.rdev)) {
+ ret = PTR_ERR(int3472->regulator.rdev);
+ goto err_free_gpio;
+ }
+
+ return 0;
+
+err_free_gpio:
+ gpiod_put(int3472->regulator.gpio);
+
+ return ret;
+}
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.c
new file mode 100644
index 000000000000..497e74fba75f
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "intel_skl_int3472_common.h"
+
+union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_handle handle = adev->handle;
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = acpi_evaluate_object(handle, id, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return ERR_PTR(-ENODEV);
+
+ obj = buffer.pointer;
+ if (!obj)
+ return ERR_PTR(-ENODEV);
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ acpi_handle_err(handle, "%s object is not an ACPI buffer\n", id);
+ kfree(obj);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return obj;
+}
+
+int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb)
+{
+ union acpi_object *obj;
+ int ret;
+
+ obj = skl_int3472_get_acpi_buffer(adev, "CLDB");
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
+ if (obj->buffer.length > sizeof(*cldb)) {
+ acpi_handle_err(adev->handle, "The CLDB buffer is too large\n");
+ ret = -EINVAL;
+ goto out_free_obj;
+ }
+
+ memcpy(cldb, obj->buffer.pointer, obj->buffer.length);
+ ret = 0;
+
+out_free_obj:
+ kfree(obj);
+ return ret;
+}
+
+static const struct acpi_device_id int3472_device_id[] = {
+ { "INT3472", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, int3472_device_id);
+
+static struct platform_driver int3472_discrete = {
+ .driver = {
+ .name = "int3472-discrete",
+ .acpi_match_table = int3472_device_id,
+ },
+ .probe = skl_int3472_discrete_probe,
+ .remove = skl_int3472_discrete_remove,
+};
+
+static struct i2c_driver int3472_tps68470 = {
+ .driver = {
+ .name = "int3472-tps68470",
+ .acpi_match_table = int3472_device_id,
+ },
+ .probe_new = skl_int3472_tps68470_probe,
+};
+
+static int skl_int3472_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&int3472_discrete);
+ if (ret)
+ return ret;
+
+ ret = i2c_register_driver(THIS_MODULE, &int3472_tps68470);
+ if (ret)
+ platform_driver_unregister(&int3472_discrete);
+
+ return ret;
+}
+module_init(skl_int3472_init);
+
+static void skl_int3472_exit(void)
+{
+ platform_driver_unregister(&int3472_discrete);
+ i2c_del_driver(&int3472_tps68470);
+}
+module_exit(skl_int3472_exit);
+
+MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver");
+MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
new file mode 100644
index 000000000000..6fdf78584219
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+
+#ifndef _INTEL_SKL_INT3472_H
+#define _INTEL_SKL_INT3472_H
+
+#include <linux/clk-provider.h>
+#include <linux/gpio/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/types.h>
+
+/* FIXME drop this once the I2C_DEV_NAME_FORMAT macro has been added to include/linux/i2c.h */
+#ifndef I2C_DEV_NAME_FORMAT
+#define I2C_DEV_NAME_FORMAT "i2c-%s"
+#endif
+
+/* PMIC GPIO Types */
+#define INT3472_GPIO_TYPE_RESET 0x00
+#define INT3472_GPIO_TYPE_POWERDOWN 0x01
+#define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b
+#define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c
+#define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d
+
+#define INT3472_PDEV_MAX_NAME_LEN 23
+#define INT3472_MAX_SENSOR_GPIOS 3
+
+#define GPIO_REGULATOR_NAME_LENGTH 21
+#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
+
+#define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86
+
+#define INT3472_REGULATOR(_name, _supply, _ops) \
+ (const struct regulator_desc) { \
+ .name = _name, \
+ .supply_name = _supply, \
+ .type = REGULATOR_VOLTAGE, \
+ .ops = _ops, \
+ .owner = THIS_MODULE, \
+ }
+
+#define to_int3472_clk(hw) \
+ container_of(hw, struct int3472_gpio_clock, clk_hw)
+
+#define to_int3472_device(clk) \
+ container_of(clk, struct int3472_discrete_device, clock)
+
+struct acpi_device;
+struct i2c_client;
+struct platform_device;
+
+struct int3472_cldb {
+ u8 version;
+ /*
+ * control logic type
+ * 0: UNKNOWN
+ * 1: DISCRETE(CRD-D)
+ * 2: PMIC TPS68470
+ * 3: PMIC uP6641
+ */
+ u8 control_logic_type;
+ u8 control_logic_id;
+ u8 sensor_card_sku;
+ u8 reserved[28];
+};
+
+struct int3472_gpio_function_remap {
+ const char *documented;
+ const char *actual;
+};
+
+struct int3472_sensor_config {
+ const char *sensor_module_name;
+ struct regulator_consumer_supply supply_map;
+ const struct int3472_gpio_function_remap *function_maps;
+};
+
+struct int3472_discrete_device {
+ struct acpi_device *adev;
+ struct device *dev;
+ struct acpi_device *sensor;
+ const char *sensor_name;
+
+ const struct int3472_sensor_config *sensor_config;
+
+ struct int3472_gpio_regulator {
+ char regulator_name[GPIO_REGULATOR_NAME_LENGTH];
+ char supply_name[GPIO_REGULATOR_SUPPLY_NAME_LENGTH];
+ struct gpio_desc *gpio;
+ struct regulator_dev *rdev;
+ struct regulator_desc rdesc;
+ } regulator;
+
+ struct int3472_gpio_clock {
+ struct clk *clk;
+ struct clk_hw clk_hw;
+ struct clk_lookup *cl;
+ struct gpio_desc *ena_gpio;
+ struct gpio_desc *led_gpio;
+ u32 frequency;
+ } clock;
+
+ 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;
+};
+
+int skl_int3472_discrete_probe(struct platform_device *pdev);
+int skl_int3472_discrete_remove(struct platform_device *pdev);
+int skl_int3472_tps68470_probe(struct i2c_client *client);
+union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev,
+ char *id);
+int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb);
+int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
+int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
+ struct acpi_resource *ares);
+
+#endif
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
new file mode 100644
index 000000000000..8c18dbff1c43
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0
+/* 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>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/uuid.h>
+
+#include "intel_skl_int3472_common.h"
+
+/*
+ * 79234640-9e10-4fea-a5c1-b5aa8b19756f
+ * This _DSM GUID returns information about the GPIO lines mapped to a
+ * discrete INT3472 device. Function number 1 returns a count of the GPIO
+ * lines that are mapped. Subsequent functions return 32 bit ints encoding
+ * information about the GPIO line, including its purpose.
+ */
+static const guid_t int3472_gpio_guid =
+ GUID_INIT(0x79234640, 0x9e10, 0x4fea,
+ 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
+
+/*
+ * 822ace8f-2814-4174-a56b-5f029fe079ee
+ * This _DSM GUID returns a string from the sensor device, which acts as a
+ * module identifier.
+ */
+static const guid_t cio2_sensor_module_guid =
+ GUID_INIT(0x822ace8f, 0x2814, 0x4174,
+ 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
+
+/*
+ * Here follows platform specific mapping information that we can pass to
+ * the functions mapping resources to the sensors. Where the sensors have
+ * a power enable pin defined in DSDT we need to provide a supply name so
+ * the sensor drivers can find the regulator. The device name will be derived
+ * from the sensor's ACPI device within the code. Optionally, we can provide a
+ * NULL terminated array of function name mappings to deal with any platform
+ * specific deviations from the documented behaviour of GPIOs.
+ *
+ * Map a GPIO function name to NULL to prevent the driver from mapping that
+ * GPIO at all.
+ */
+
+static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = {
+ { "reset", NULL },
+ { "powerdown", "reset" },
+ { }
+};
+
+static const struct int3472_sensor_config int3472_sensor_configs[] = {
+ /* Lenovo Miix 510-12ISK - OV2680, Front */
+ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps },
+ /* Lenovo Miix 510-12ISK - OV5648, Rear */
+ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL },
+ /* Surface Go 1&2 - OV5693, Front */
+ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL },
+};
+
+static const struct int3472_sensor_config *
+skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472)
+{
+ union acpi_object *obj;
+ unsigned int i;
+
+ obj = acpi_evaluate_dsm_typed(int3472->sensor->handle,
+ &cio2_sensor_module_guid, 0x00,
+ 0x01, NULL, ACPI_TYPE_STRING);
+
+ if (!obj) {
+ dev_err(int3472->dev,
+ "Failed to get sensor module string from _DSM\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (obj->string.type != ACPI_TYPE_STRING) {
+ dev_err(int3472->dev,
+ "Sensor _DSM returned a non-string value\n");
+
+ ACPI_FREE(obj);
+ return ERR_PTR(-EINVAL);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) {
+ if (!strcmp(int3472_sensor_configs[i].sensor_module_name,
+ obj->string.pointer))
+ break;
+ }
+
+ ACPI_FREE(obj);
+
+ if (i >= ARRAY_SIZE(int3472_sensor_configs))
+ return ERR_PTR(-EINVAL);
+
+ return &int3472_sensor_configs[i];
+}
+
+static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
+ struct acpi_resource *ares,
+ const char *func, u32 polarity)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ const struct int3472_sensor_config *sensor_config;
+ struct gpiod_lookup *table_entry;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_status status;
+ int ret;
+
+ if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
+ dev_warn(int3472->dev, "Too many GPIOs mapped\n");
+ return -EINVAL;
+ }
+
+ sensor_config = int3472->sensor_config;
+ if (!IS_ERR(sensor_config) && sensor_config->function_maps) {
+ const struct int3472_gpio_function_remap *remap;
+
+ for (remap = sensor_config->function_maps; remap->documented; remap++) {
+ if (!strcmp(func, remap->documented)) {
+ func = remap->actual;
+ break;
+ }
+ }
+ }
+
+ /* Functions mapped to NULL should not be mapped to the sensor */
+ if (!func)
+ return 0;
+
+ status = acpi_get_handle(NULL, path, &handle);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ ret = acpi_bus_get_device(handle, &adev);
+ if (ret)
+ return -ENODEV;
+
+ table_entry = &int3472->gpios.table[int3472->n_sensor_gpios];
+ table_entry->key = acpi_dev_name(adev);
+ table_entry->chip_hwnum = ares->data.gpio.pin_table[0];
+ table_entry->con_id = func;
+ table_entry->idx = 0;
+ table_entry->flags = polarity;
+
+ int3472->n_sensor_gpios++;
+
+ return 0;
+}
+
+static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
+ struct acpi_resource *ares, u8 type)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ struct gpio_desc *gpio;
+
+ switch (type) {
+ case INT3472_GPIO_TYPE_CLK_ENABLE:
+ gpio = acpi_get_and_request_gpiod(path, ares->data.gpio.pin_table[0],
+ "int3472,clk-enable");
+ if (IS_ERR(gpio))
+ return (PTR_ERR(gpio));
+
+ int3472->clock.ena_gpio = gpio;
+ break;
+ case INT3472_GPIO_TYPE_PRIVACY_LED:
+ gpio = acpi_get_and_request_gpiod(path, ares->data.gpio.pin_table[0],
+ "int3472,privacy-led");
+ if (IS_ERR(gpio))
+ return (PTR_ERR(gpio));
+
+ int3472->clock.led_gpio = gpio;
+ break;
+ default:
+ dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * skl_int3472_handle_gpio_resources: Map PMIC resources to consuming sensor
+ * @ares: A pointer to a &struct acpi_resource
+ * @data: A pointer to a &struct int3472_discrete_device
+ *
+ * This function handles GPIO resources that are against an INT3472
+ * ACPI device, by checking the value of the corresponding _DSM entry.
+ * This will return a 32bit int, where the lowest byte represents the
+ * function of the GPIO pin:
+ *
+ * 0x00 Reset
+ * 0x01 Power down
+ * 0x0b Power enable
+ * 0x0c Clock enable
+ * 0x0d Privacy LED
+ *
+ * There are some known platform specific quirks where that does not quite
+ * hold up; for example where a pin with type 0x01 (Power down) is mapped to
+ * a sensor pin that performs a reset function or entries in _CRS and _DSM that
+ * do not actually correspond to a physical connection. These will be handled
+ * by the mapping sub-functions.
+ *
+ * GPIOs will either be mapped directly to the sensor device or else used
+ * to create clocks and regulators via the usual frameworks.
+ *
+ * Return:
+ * * 1 - To continue the loop
+ * * 0 - When all resources found are handled properly.
+ * * -EINVAL - If the resource is not a GPIO IO resource
+ * * -ENODEV - If the resource has no corresponding _DSM entry
+ * * -Other - Errors propagated from one of the sub-functions.
+ */
+static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
+ void *data)
+{
+ struct int3472_discrete_device *int3472 = data;
+ struct acpi_resource_gpio *agpio;
+ union acpi_object *obj;
+ const char *err_msg;
+ int ret;
+ u8 type;
+
+ if (!acpi_gpio_get_io_resource(ares, &agpio))
+ return 1;
+
+ /*
+ * ngpios + 2 because the index of this _DSM function is 1-based and
+ * the first function is just a count.
+ */
+ obj = acpi_evaluate_dsm_typed(int3472->adev->handle,
+ &int3472_gpio_guid, 0x00,
+ int3472->ngpios + 2,
+ NULL, ACPI_TYPE_INTEGER);
+
+ if (!obj) {
+ dev_warn(int3472->dev, "No _DSM entry for GPIO pin %u\n",
+ ares->data.gpio.pin_table[0]);
+ return 1;
+ }
+
+ type = obj->integer.value & 0xff;
+
+ switch (type) {
+ case INT3472_GPIO_TYPE_RESET:
+ ret = skl_int3472_map_gpio_to_sensor(int3472, ares, "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, ares,
+ "powerdown",
+ GPIO_ACTIVE_LOW);
+ if (ret)
+ err_msg = "Failed to map powerdown pin to sensor\n";
+
+ break;
+ case INT3472_GPIO_TYPE_CLK_ENABLE:
+ case INT3472_GPIO_TYPE_PRIVACY_LED:
+ ret = skl_int3472_map_gpio_to_clk(int3472, ares, type);
+ if (ret)
+ err_msg = "Failed to map GPIO to clock\n";
+
+ break;
+ case INT3472_GPIO_TYPE_POWER_ENABLE:
+ ret = skl_int3472_register_regulator(int3472, ares);
+ if (ret)
+ err_msg = "Failed to map regulator to sensor\n";
+
+ break;
+ default:
+ dev_warn(int3472->dev,
+ "GPIO type 0x%02x unknown; the sensor may not work\n",
+ type);
+ ret = 1;
+ break;
+ }
+
+ int3472->ngpios++;
+ ACPI_FREE(obj);
+
+ if (ret)
+ return dev_err_probe(int3472->dev, ret, err_msg);
+
+ return 0;
+}
+
+static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
+{
+ LIST_HEAD(resource_list);
+ int ret;
+
+ /*
+ * No error check, because not having a sensor config is not necessarily
+ * a failure mode.
+ */
+ int3472->sensor_config = skl_int3472_get_sensor_module_config(int3472);
+
+ ret = acpi_dev_get_resources(int3472->adev, &resource_list,
+ skl_int3472_handle_gpio_resources,
+ int3472);
+ if (ret)
+ goto out_free_res_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)
+ goto out_free_res_list;
+ } 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);
+
+out_free_res_list:
+ acpi_dev_free_resource_list(&resource_list);
+
+ return ret;
+}
+
+int skl_int3472_discrete_probe(struct platform_device *pdev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+ struct int3472_discrete_device *int3472;
+ struct int3472_cldb cldb;
+ int ret;
+
+ ret = skl_int3472_fill_cldb(adev, &cldb);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't fill CLDB structure\n");
+ return ret;
+ }
+
+ if (cldb.control_logic_type != 1) {
+ dev_err(&pdev->dev, "Unsupported control logic type %u\n",
+ cldb.control_logic_type);
+ return -EINVAL;
+ }
+
+ /* Max num GPIOs we've seen plus a terminator */
+ int3472 = devm_kzalloc(&pdev->dev, struct_size(int3472, gpios.table,
+ INT3472_MAX_SENSOR_GPIOS + 1), GFP_KERNEL);
+ if (!int3472)
+ return -ENOMEM;
+
+ int3472->adev = adev;
+ int3472->dev = &pdev->dev;
+ platform_set_drvdata(pdev, int3472);
+
+ int3472->sensor = acpi_dev_get_first_consumer_dev(adev);
+ if (!int3472->sensor) {
+ dev_err(&pdev->dev, "INT3472 seems to have no dependents.\n");
+ return -ENODEV;
+ }
+
+ int3472->sensor_name = devm_kasprintf(int3472->dev, GFP_KERNEL,
+ I2C_DEV_NAME_FORMAT,
+ acpi_dev_name(int3472->sensor));
+ if (!int3472->sensor_name) {
+ ret = -ENOMEM;
+ goto err_put_sensor;
+ }
+
+ /*
+ * Initialising this list means we can call gpiod_remove_lookup_table()
+ * in failure paths without issue.
+ */
+ INIT_LIST_HEAD(&int3472->gpios.list);
+
+ ret = skl_int3472_parse_crs(int3472);
+ if (ret) {
+ skl_int3472_discrete_remove(pdev);
+ return ret;
+ }
+
+ return 0;
+
+err_put_sensor:
+ acpi_dev_put(int3472->sensor);
+
+ return ret;
+}
+
+int skl_int3472_discrete_remove(struct platform_device *pdev)
+{
+ struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev);
+
+ gpiod_remove_lookup_table(&int3472->gpios);
+ regulator_unregister(int3472->regulator.rdev);
+ clk_unregister(int3472->clock.clk);
+
+ if (int3472->clock.cl)
+ clkdev_drop(int3472->clock.cl);
+
+ gpiod_put(int3472->regulator.gpio);
+ gpiod_put(int3472->clock.ena_gpio);
+ gpiod_put(int3472->clock.led_gpio);
+
+ return 0;
+}
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_tps68470.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_tps68470.c
new file mode 100644
index 000000000000..c05b4cf502fe
--- /dev/null
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_tps68470.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps68470.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "intel_skl_int3472_common.h"
+
+#define DESIGNED_FOR_CHROMEOS 1
+#define DESIGNED_FOR_WINDOWS 2
+
+static const struct mfd_cell tps68470_cros[] = {
+ { .name = "tps68470-gpio" },
+ { .name = "tps68470_pmic_opregion" },
+};
+
+static const struct mfd_cell tps68470_win[] = {
+ { .name = "tps68470-gpio" },
+ { .name = "tps68470-clk" },
+ { .name = "tps68470-regulator" },
+};
+
+static const struct regmap_config tps68470_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPS68470_REG_MAX,
+};
+
+static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
+{
+ unsigned int version;
+ int ret;
+
+ /* Force software reset */
+ ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
+ if (ret) {
+ dev_err(dev, "Failed to read revision register: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
+
+ return 0;
+}
+
+/** skl_int3472_tps68470_calc_type: Check what platform a device is designed for
+ * @adev: A pointer to a &struct acpi_device
+ *
+ * Check CLDB buffer against the PMIC's adev. If present, then we check
+ * the value of control_logic_type field and follow one of the
+ * following scenarios:
+ *
+ * 1. No CLDB - likely ACPI tables designed for ChromeOS. We
+ * create platform devices for the GPIOs and OpRegion drivers.
+ *
+ * 2. CLDB, with control_logic_type = 2 - probably ACPI tables
+ * made for Windows 2-in-1 platforms. Register pdevs for GPIO,
+ * Clock and Regulator drivers to bind to.
+ *
+ * 3. Any other value in control_logic_type, we should never have
+ * gotten to this point; fail probe and return.
+ *
+ * Return:
+ * * 1 Device intended for ChromeOS
+ * * 2 Device intended for Windows
+ * * -EINVAL Where @adev has an object named CLDB but it does not conform to
+ * our expectations
+ */
+static int skl_int3472_tps68470_calc_type(struct acpi_device *adev)
+{
+ struct int3472_cldb cldb = { 0 };
+ int ret;
+
+ /*
+ * A CLDB buffer that exists, but which does not match our expectations
+ * should trigger an error so we don't blindly continue.
+ */
+ ret = skl_int3472_fill_cldb(adev, &cldb);
+ if (ret && ret != -ENODEV)
+ return ret;
+
+ if (ret)
+ return DESIGNED_FOR_CHROMEOS;
+
+ if (cldb.control_logic_type != 2)
+ return -EINVAL;
+
+ return DESIGNED_FOR_WINDOWS;
+}
+
+int skl_int3472_tps68470_probe(struct i2c_client *client)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&client->dev);
+ struct regmap *regmap;
+ int device_type;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ i2c_set_clientdata(client, regmap);
+
+ ret = tps68470_chip_init(&client->dev, regmap);
+ if (ret < 0) {
+ dev_err(&client->dev, "TPS68470 init error %d\n", ret);
+ return ret;
+ }
+
+ device_type = skl_int3472_tps68470_calc_type(adev);
+ switch (device_type) {
+ case DESIGNED_FOR_WINDOWS:
+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+ tps68470_win, ARRAY_SIZE(tps68470_win),
+ NULL, 0, NULL);
+ break;
+ case DESIGNED_FOR_CHROMEOS:
+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+ tps68470_cros, ARRAY_SIZE(tps68470_cros),
+ NULL, 0, NULL);
+ break;
+ default:
+ dev_err(&client->dev, "Failed to add MFD devices\n");
+ return device_type;
+ }
+
+ return ret;
+}
--
2.33.0
From 06899d0ea5e8fa1e6d3ab17c3b59e9d9bdea4980 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 3 Jun 2021 23:40:07 +0100
Subject: [PATCH] mfd: tps68470: Remove tps68470 MFD driver
This driver only covered one scenario in which ACPI devices with _HID
INT3472 are found, and its functionality has been taken over by the
intel-skl-int3472 module, so remove it.
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/20210603224007.120560-7-djrscally@gmail.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/acpi/pmic/Kconfig | 2 +-
drivers/gpio/Kconfig | 2 +-
drivers/mfd/Kconfig | 18 --------
drivers/mfd/Makefile | 1 -
drivers/mfd/tps68470.c | 97 ---------------------------------------
5 files changed, 2 insertions(+), 118 deletions(-)
delete mode 100644 drivers/mfd/tps68470.c
diff --git a/drivers/acpi/pmic/Kconfig b/drivers/acpi/pmic/Kconfig
index 56bbcb2ce61b..f84b8f6038dc 100644
--- a/drivers/acpi/pmic/Kconfig
+++ b/drivers/acpi/pmic/Kconfig
@@ -52,7 +52,7 @@ endif # PMIC_OPREGION
config TPS68470_PMIC_OPREGION
bool "ACPI operation region support for TPS68470 PMIC"
- depends on MFD_TPS68470
+ depends on INTEL_SKL_INT3472
help
This config adds ACPI operation region support for TI TPS68470 PMIC.
TPS68470 device is an advanced power management unit that powers
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3c69b785cb79..63b84ba161dc 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1367,7 +1367,7 @@ config GPIO_TPS65912
config GPIO_TPS68470
bool "TPS68470 GPIO"
- depends on MFD_TPS68470
+ depends on INTEL_SKL_INT3472
help
Select this option to enable GPIO driver for the TPS68470
chip family.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 5c408c1dc58c..ace0d1b8f55c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1500,24 +1500,6 @@ config MFD_TPS65217
This driver can also be built as a module. If so, the module
will be called tps65217.
-config MFD_TPS68470
- bool "TI TPS68470 Power Management / LED chips"
- depends on ACPI && PCI && I2C=y
- depends on I2C_DESIGNWARE_PLATFORM=y
- select MFD_CORE
- select REGMAP_I2C
- help
- If you say yes here you get support for the TPS68470 series of
- Power Management / LED chips.
-
- These include voltage regulators, LEDs and other features
- that are often used in portable devices.
-
- This option is a bool as it provides an ACPI operation
- region, which must be available before any of the devices
- using this are probed. This option also configures the
- designware-i2c driver to be built-in, for the same reason.
-
config MFD_TI_LP873X
tristate "TI LP873X Power Management IC"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 4f6d2b8a5f76..8b322d89a0c5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -105,7 +105,6 @@ obj-$(CONFIG_MFD_TPS65910) += tps65910.o
obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
-obj-$(CONFIG_MFD_TPS68470) += tps68470.o
obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o
diff --git a/drivers/mfd/tps68470.c b/drivers/mfd/tps68470.c
deleted file mode 100644
index 4a4df4ffd18c..000000000000
--- a/drivers/mfd/tps68470.c
+++ /dev/null
@@ -1,97 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * TPS68470 chip Parent driver
- *
- * Copyright (C) 2017 Intel Corporation
- *
- * Authors:
- * Rajmohan Mani <rajmohan.mani@intel.com>
- * Tianshu Qiu <tian.shu.qiu@intel.com>
- * Jian Xu Zheng <jian.xu.zheng@intel.com>
- * Yuning Pu <yuning.pu@intel.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/tps68470.h>
-#include <linux/regmap.h>
-
-static const struct mfd_cell tps68470s[] = {
- { .name = "tps68470-gpio" },
- { .name = "tps68470_pmic_opregion" },
-};
-
-static const struct regmap_config tps68470_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = TPS68470_REG_MAX,
-};
-
-static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
-{
- unsigned int version;
- int ret;
-
- /* Force software reset */
- ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
- if (ret)
- return ret;
-
- ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
- if (ret) {
- dev_err(dev, "Failed to read revision register: %d\n", ret);
- return ret;
- }
-
- dev_info(dev, "TPS68470 REVID: 0x%x\n", version);
-
- return 0;
-}
-
-static int tps68470_probe(struct i2c_client *client)
-{
- struct device *dev = &client->dev;
- struct regmap *regmap;
- int ret;
-
- regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(dev, "devm_regmap_init_i2c Error %ld\n",
- PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
-
- i2c_set_clientdata(client, regmap);
-
- ret = tps68470_chip_init(dev, regmap);
- if (ret < 0) {
- dev_err(dev, "TPS68470 Init Error %d\n", ret);
- return ret;
- }
-
- ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, tps68470s,
- ARRAY_SIZE(tps68470s), NULL, 0, NULL);
- if (ret < 0) {
- dev_err(dev, "devm_mfd_add_devices failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static const struct acpi_device_id tps68470_acpi_ids[] = {
- {"INT3472"},
- {},
-};
-
-static struct i2c_driver tps68470_driver = {
- .driver = {
- .name = "tps68470",
- .acpi_match_table = tps68470_acpi_ids,
- },
- .probe_new = tps68470_probe,
-};
-builtin_i2c_driver(tps68470_driver);
--
2.33.0
From 375118c70ce1ecc24b7729f0f48c487fa24418aa Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Fri, 18 Jun 2021 15:55:10 +0300
Subject: [PATCH] platform/x86: intel_skl_int3472: Free ACPI device resources
after use
We may free ACPI device resources immediately after use.
Refactor skl_int3472_parse_crs() accordingly.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Tested-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/20210618125516.53510-2-andriy.shevchenko@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../x86/intel-int3472/intel_skl_int3472_discrete.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
index 8c18dbff1c43..48a00a1f4fb6 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
@@ -308,8 +308,10 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
ret = acpi_dev_get_resources(int3472->adev, &resource_list,
skl_int3472_handle_gpio_resources,
int3472);
- if (ret)
- goto out_free_res_list;
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&resource_list);
/*
* If we find no clock enable GPIO pin then the privacy LED won't work.
@@ -319,7 +321,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
if (int3472->clock.ena_gpio) {
ret = skl_int3472_register_clock(int3472);
if (ret)
- goto out_free_res_list;
+ return ret;
} else {
if (int3472->clock.led_gpio)
dev_warn(int3472->dev,
@@ -329,10 +331,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
int3472->gpios.dev_id = int3472->sensor_name;
gpiod_add_lookup_table(&int3472->gpios);
-out_free_res_list:
- acpi_dev_free_resource_list(&resource_list);
-
- return ret;
+ return 0;
}
int skl_int3472_discrete_probe(struct platform_device *pdev)
--
2.33.0
From a15fa9ed6d6509ad6e11721f34bfb79f8e292128 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Fri, 18 Jun 2021 15:55:11 +0300
Subject: [PATCH] platform/x86: intel_skl_int3472: Fix dependencies (drop
CLKDEV_LOOKUP)
Besides the fact that COMMON_CLK selects CLKDEV_LOOKUP, the latter
is going to be removed from clock framework.
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20210618125516.53510-3-andriy.shevchenko@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
drivers/platform/x86/intel-int3472/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/platform/x86/intel-int3472/Kconfig b/drivers/platform/x86/intel-int3472/Kconfig
index c112878e833b..62e5d4cf9ee5 100644
--- a/drivers/platform/x86/intel-int3472/Kconfig
+++ b/drivers/platform/x86/intel-int3472/Kconfig
@@ -1,7 +1,7 @@
config INTEL_SKL_INT3472
tristate "Intel SkyLake ACPI INT3472 Driver"
depends on ACPI
- depends on COMMON_CLK && CLKDEV_LOOKUP
+ depends on COMMON_CLK
depends on I2C
depends on GPIOLIB
depends on REGULATOR
--
2.33.0
From 687cd00f086741323a81dfb1290a3723b9485ce2 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Fri, 18 Jun 2021 15:55:12 +0300
Subject: [PATCH] platform/x86: intel_skl_int3472: Use ACPI GPIO resource
directly
When we call acpi_gpio_get_io_resource(), the output will be
the pointer to the ACPI GPIO resource. Use it directly instead of
dereferencing the generic resource.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Tested-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/20210618125516.53510-4-andriy.shevchenko@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../intel_skl_int3472_clk_and_regulator.c | 7 ++---
.../intel-int3472/intel_skl_int3472_common.h | 2 +-
.../intel_skl_int3472_discrete.c | 28 +++++++++----------
3 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
index ceee860e2c07..49ea1e86c193 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
@@ -131,10 +131,10 @@ int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
}
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
- struct acpi_resource *ares)
+ struct acpi_resource_gpio *agpio)
{
- char *path = ares->data.gpio.resource_source.string_ptr;
const struct int3472_sensor_config *sensor_config;
+ char *path = agpio->resource_source.string_ptr;
struct regulator_consumer_supply supply_map;
struct regulator_init_data init_data = { };
struct regulator_config cfg = { };
@@ -168,8 +168,7 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
int3472->regulator.supply_name,
&int3472_gpio_regulator_ops);
- int3472->regulator.gpio = acpi_get_and_request_gpiod(path,
- ares->data.gpio.pin_table[0],
+ int3472->regulator.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0],
"int3472,regulator");
if (IS_ERR(int3472->regulator.gpio)) {
dev_err(int3472->dev, "Failed to get regulator GPIO line\n");
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
index 6fdf78584219..765e01ec1604 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
@@ -113,6 +113,6 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev,
int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb);
int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
- struct acpi_resource *ares);
+ struct acpi_resource_gpio *agpio);
#endif
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
index 48a00a1f4fb6..fd681d2a73fe 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
@@ -103,11 +103,11 @@ skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472)
}
static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
- struct acpi_resource *ares,
+ struct acpi_resource_gpio *agpio,
const char *func, u32 polarity)
{
- char *path = ares->data.gpio.resource_source.string_ptr;
const struct int3472_sensor_config *sensor_config;
+ char *path = agpio->resource_source.string_ptr;
struct gpiod_lookup *table_entry;
struct acpi_device *adev;
acpi_handle handle;
@@ -145,7 +145,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
table_entry = &int3472->gpios.table[int3472->n_sensor_gpios];
table_entry->key = acpi_dev_name(adev);
- table_entry->chip_hwnum = ares->data.gpio.pin_table[0];
+ table_entry->chip_hwnum = agpio->pin_table[0];
table_entry->con_id = func;
table_entry->idx = 0;
table_entry->flags = polarity;
@@ -156,23 +156,22 @@ 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 *ares, u8 type)
+ struct acpi_resource_gpio *agpio, u8 type)
{
- char *path = ares->data.gpio.resource_source.string_ptr;
+ 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, ares->data.gpio.pin_table[0],
- "int3472,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;
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
- gpio = acpi_get_and_request_gpiod(path, ares->data.gpio.pin_table[0],
- "int3472,privacy-led");
+ gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led");
if (IS_ERR(gpio))
return (PTR_ERR(gpio));
@@ -242,7 +241,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
if (!obj) {
dev_warn(int3472->dev, "No _DSM entry for GPIO pin %u\n",
- ares->data.gpio.pin_table[0]);
+ agpio->pin_table[0]);
return 1;
}
@@ -250,15 +249,14 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
switch (type) {
case INT3472_GPIO_TYPE_RESET:
- ret = skl_int3472_map_gpio_to_sensor(int3472, ares, "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, ares,
- "powerdown",
+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown",
GPIO_ACTIVE_LOW);
if (ret)
err_msg = "Failed to map powerdown pin to sensor\n";
@@ -266,13 +264,13 @@ 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, ares, type);
+ ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type);
if (ret)
err_msg = "Failed to map GPIO to clock\n";
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
- ret = skl_int3472_register_regulator(int3472, ares);
+ ret = skl_int3472_register_regulator(int3472, agpio);
if (ret)
err_msg = "Failed to map regulator to sensor\n";
--
2.33.0
From 110d7885979dad5b0d6beb81369d93c3cc38d0d8 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Fri, 18 Jun 2021 15:55:13 +0300
Subject: [PATCH] platform/x86: intel_skl_int3472: Provide
skl_int3472_unregister_regulator()
For the sake of APIs to be properly layered provide
skl_int3472_unregister_regulator().
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Tested-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/20210618125516.53510-5-andriy.shevchenko@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c | 6 ++++++
.../platform/x86/intel-int3472/intel_skl_int3472_common.h | 2 ++
.../platform/x86/intel-int3472/intel_skl_int3472_discrete.c | 4 ++--
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
index 49ea1e86c193..60c7128f44ee 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
@@ -193,3 +193,9 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
return ret;
}
+
+void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472)
+{
+ regulator_unregister(int3472->regulator.rdev);
+ gpiod_put(int3472->regulator.gpio);
+}
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
index 765e01ec1604..50f73c6eab44 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
@@ -112,7 +112,9 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev,
char *id);
int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb);
int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
+
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);
#endif
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
index fd681d2a73fe..2638d375e226 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
@@ -400,15 +400,15 @@ int skl_int3472_discrete_remove(struct platform_device *pdev)
struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev);
gpiod_remove_lookup_table(&int3472->gpios);
- regulator_unregister(int3472->regulator.rdev);
clk_unregister(int3472->clock.clk);
if (int3472->clock.cl)
clkdev_drop(int3472->clock.cl);
- gpiod_put(int3472->regulator.gpio);
gpiod_put(int3472->clock.ena_gpio);
gpiod_put(int3472->clock.led_gpio);
+ skl_int3472_unregister_regulator(int3472);
+
return 0;
}
--
2.33.0
From 41625860cfb0264e6b3f575bd3c7330ce3df59e0 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Fri, 18 Jun 2021 15:55:14 +0300
Subject: [PATCH] platform/x86: intel_skl_int3472: Provide
skl_int3472_unregister_clock()
For the sake of APIs to be properly layered provide
skl_int3472_unregister_clock().
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Tested-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/20210618125516.53510-6-andriy.shevchenko@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c | 6 ++++++
.../platform/x86/intel-int3472/intel_skl_int3472_common.h | 2 ++
.../platform/x86/intel-int3472/intel_skl_int3472_discrete.c | 5 ++---
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
index 60c7128f44ee..1700e7557a82 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_clk_and_regulator.c
@@ -130,6 +130,12 @@ int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
return ret;
}
+void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)
+{
+ clkdev_drop(int3472->clock.cl);
+ clk_unregister(int3472->clock.clk);
+}
+
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio)
{
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
index 50f73c6eab44..714fde73b524 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_common.h
@@ -111,7 +111,9 @@ int skl_int3472_tps68470_probe(struct i2c_client *client);
union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev,
char *id);
int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb);
+
int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
+void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio);
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
index 2638d375e226..17c6fe830765 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
@@ -400,10 +400,9 @@ int skl_int3472_discrete_remove(struct platform_device *pdev)
struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev);
gpiod_remove_lookup_table(&int3472->gpios);
- clk_unregister(int3472->clock.clk);
- if (int3472->clock.cl)
- clkdev_drop(int3472->clock.cl);
+ if (int3472->clock.ena_gpio)
+ skl_int3472_unregister_clock(int3472);
gpiod_put(int3472->clock.ena_gpio);
gpiod_put(int3472->clock.led_gpio);
--
2.33.0
From 7be2b7369e4bb716b797c5532f8381d73fd941e0 Mon Sep 17 00:00:00 2001
From: Dan Carpenter <dan.carpenter@oracle.com>
Date: Fri, 25 Jun 2021 16:01:04 +0300
Subject: [PATCH] platform/x86: intel_skl_int3472: Uninitialized variable in
skl_int3472_handle_gpio_resources()
This function returns negative error codes, zero (to indicate that
everything has been completed successfully) and one (to indicate that
more resources need to be handled still).
This code prints an uninitialized error message when the function
returns one which potentially leads to an Oops.
Fixes: 5de691bffe57 ("platform/x86: Add intel_skl_int3472 driver")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Link: https://lore.kernel.org/r/YNXTkLNtiTDlFlZa@mwanda
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Patchset: cameras
---
.../platform/x86/intel-int3472/intel_skl_int3472_discrete.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
index 17c6fe830765..9fe0a2527e1c 100644
--- a/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel-int3472/intel_skl_int3472_discrete.c
@@ -286,10 +286,10 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
int3472->ngpios++;
ACPI_FREE(obj);
- if (ret)
+ if (ret < 0)
return dev_err_probe(int3472->dev, ret, err_msg);
- return 0;
+ return ret;
}
static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
--
2.33.0
From 0487dd4fbc6e26a0e03cae276049d094cfa6c448 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 5 Apr 2021 23:56:53 +0100
Subject: [PATCH] media: ipu3-cio2: Toggle sensor streaming in pm runtime ops
The .suspend() and .resume() runtime_pm operations for the ipu3-cio2
driver currently do not handle the sensor's stream. Setting .s_stream() on
or off for the sensor subdev means that sensors will pause and resume the
stream at the appropriate time even if their drivers don't implement those
operations.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index fecef85bd62e..9dafb9470708 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1973,12 +1973,19 @@ static int __maybe_unused cio2_suspend(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
struct cio2_queue *q = cio2->cur_queue;
+ int r;
dev_dbg(dev, "cio2 suspend\n");
if (!cio2->streaming)
return 0;
/* Stop stream */
+ r = v4l2_subdev_call(q->sensor, video, s_stream, 0);
+ if (r) {
+ dev_err(dev, "failed to stop sensor streaming\n");
+ return r;
+ }
+
cio2_hw_exit(cio2, q);
synchronize_irq(pci_dev->irq);
@@ -2013,8 +2020,14 @@ static int __maybe_unused cio2_resume(struct device *dev)
}
r = cio2_hw_init(cio2, q);
- if (r)
+ if (r) {
dev_err(dev, "fail to init cio2 hw\n");
+ return r;
+ }
+
+ r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
+ if (r)
+ dev_err(dev, "fail to start sensor streaming\n");
return r;
}
--
2.33.0
From d4d0ceeedc1d0f728e8eca398e88bfcb848cb99d Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 5 Apr 2021 23:56:54 +0100
Subject: [PATCH] media: i2c: Add support for ov5693 sensor
The OV5693 is a 5 Mpx CMOS image sensor, connected via MIPI CSI-2. The
chip is capable of a single lane configuration, but currently only two
lanes are supported.
Most of the sensor's features are supported, with the main exception
being the lens correction algorithm.
The driver provides all mandatory, optional and recommended V4L2 controls
for maximum compatibility with libcamera.
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/ov5693.c | 1557 ++++++++++++++++++++++++++++++++++++
4 files changed, 1576 insertions(+)
create mode 100644 drivers/media/i2c/ov5693.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 1db7311d78a6..567a5b64d297 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13563,6 +13563,13 @@ S: Maintained
T: git git://linuxtv.org/media_tree.git
F: drivers/media/i2c/ov5675.c
+OMNIVISION OV5693 SENSOR 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/ov5693.c
+
OMNIVISION OV5695 SENSOR DRIVER
M: Shunqian Zheng <zhengsq@rock-chips.com>
L: linux-media@vger.kernel.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 462c0e059754..2893e74af99a 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -999,6 +999,17 @@ config VIDEO_OV5675
To compile this driver as a module, choose M here: the
module will be called ov5675.
+config VIDEO_OV5693
+ tristate "OmniVision OV5693 sensor support"
+ depends on I2C && VIDEO_V4L2
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OV5693 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5693.
+
config VIDEO_OV5695
tristate "OmniVision OV5695 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 0c067beca066..7d0884bc89f1 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV5648) += ov5648.o
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
obj-$(CONFIG_VIDEO_OV5675) += ov5675.o
+obj-$(CONFIG_VIDEO_OV5693) += ov5693.o
obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7251) += ov7251.o
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
new file mode 100644
index 000000000000..276f625d4d23
--- /dev/null
+++ b/drivers/media/i2c/ov5693.c
@@ -0,0 +1,1557 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * Adapted from the atomisp-ov5693 driver, with contributions from:
+ *
+ * Daniel Scally
+ * Jean-Michel Hautbois
+ * Fabian Wuthrich
+ * Tsuchiya Yuto
+ * Jordan Hand
+ * Jake Day
+ */
+
+#include <asm/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+/* System Control */
+#define OV5693_SW_RESET_REG 0x0103
+#define OV5693_SW_STREAM_REG 0x0100
+#define OV5693_START_STREAMING 0x01
+#define OV5693_STOP_STREAMING 0x00
+#define OV5693_SW_RESET 0x01
+
+#define OV5693_REG_CHIP_ID_H 0x300a
+#define OV5693_REG_CHIP_ID_L 0x300b
+/* Yes, this is right. The datasheet for the OV5693 gives its ID as 0x5690 */
+#define OV5693_CHIP_ID 0x5690
+
+/* Exposure */
+#define OV5693_EXPOSURE_L_CTRL_HH_REG 0x3500
+#define OV5693_EXPOSURE_L_CTRL_H_REG 0x3501
+#define OV5693_EXPOSURE_L_CTRL_L_REG 0x3502
+#define OV5693_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(14, 12)) >> 12)
+#define OV5693_EXPOSURE_CTRL_H(v) (((v) & GENMASK(11, 4)) >> 4)
+#define OV5693_EXPOSURE_CTRL_L(v) (((v) & GENMASK(3, 0)) << 4)
+#define OV5693_INTEGRATION_TIME_MARGIN 8
+#define OV5693_EXPOSURE_MIN 1
+#define OV5693_EXPOSURE_STEP 1
+
+/* Analogue Gain */
+#define OV5693_GAIN_CTRL_H_REG 0x350a
+#define OV5693_GAIN_CTRL_H(v) (((v) >> 4) & GENMASK(2, 0))
+#define OV5693_GAIN_CTRL_L_REG 0x350b
+#define OV5693_GAIN_CTRL_L(v) (((v) << 4) & GENMASK(7, 4))
+#define OV5693_GAIN_MIN 1
+#define OV5693_GAIN_MAX 127
+#define OV5693_GAIN_DEF 8
+#define OV5693_GAIN_STEP 1
+
+/* Digital Gain */
+#define OV5693_MWB_RED_GAIN_H_REG 0x3400
+#define OV5693_MWB_RED_GAIN_L_REG 0x3401
+#define OV5693_MWB_GREEN_GAIN_H_REG 0x3402
+#define OV5693_MWB_GREEN_GAIN_L_REG 0x3403
+#define OV5693_MWB_BLUE_GAIN_H_REG 0x3404
+#define OV5693_MWB_BLUE_GAIN_L_REG 0x3405
+#define OV5693_MWB_GAIN_H_CTRL(v) (((v) >> 8) & GENMASK(3, 0))
+#define OV5693_MWB_GAIN_L_CTRL(v) ((v) & GENMASK(7, 0))
+#define OV5693_MWB_GAIN_MAX 0x0fff
+#define OV5693_DIGITAL_GAIN_MIN 1
+#define OV5693_DIGITAL_GAIN_MAX 4095
+#define OV5693_DIGITAL_GAIN_DEF 1024
+#define OV5693_DIGITAL_GAIN_STEP 1
+
+/* Timing and Format */
+#define OV5693_CROP_START_X_H_REG 0x3800
+#define OV5693_CROP_START_X_H(v) (((v) & GENMASK(12, 8)) >> 8)
+#define OV5693_CROP_START_X_L_REG 0x3801
+#define OV5693_CROP_START_X_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_CROP_START_Y_H_REG 0x3802
+#define OV5693_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
+#define OV5693_CROP_START_Y_L_REG 0x3803
+#define OV5693_CROP_START_Y_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_CROP_END_X_H_REG 0x3804
+#define OV5693_CROP_END_X_H(v) (((v) & GENMASK(12, 8)) >> 8)
+#define OV5693_CROP_END_X_L_REG 0x3805
+#define OV5693_CROP_END_X_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_CROP_END_Y_H_REG 0x3806
+#define OV5693_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
+#define OV5693_CROP_END_Y_L_REG 0x3807
+#define OV5693_CROP_END_Y_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_OUTPUT_SIZE_X_H_REG 0x3808
+#define OV5693_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_OUTPUT_SIZE_X_L_REG 0x3809
+#define OV5693_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_OUTPUT_SIZE_Y_H_REG 0x380a
+#define OV5693_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_OUTPUT_SIZE_Y_L_REG 0x380b
+#define OV5693_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_TIMING_HTS_H_REG 0x380c
+#define OV5693_TIMING_HTS_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_TIMING_HTS_L_REG 0x380d
+#define OV5693_TIMING_HTS_L(v) ((v) & GENMASK(7, 0))
+#define OV5693_FIXED_PPL 2688U
+
+#define OV5693_TIMING_VTS_H_REG 0x380e
+#define OV5693_TIMING_VTS_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_TIMING_VTS_L_REG 0x380f
+#define OV5693_TIMING_VTS_L(v) ((v) & GENMASK(7, 0))
+#define OV5693_TIMING_MAX_VTS 0xffff
+#define OV5693_TIMING_MIN_VTS 0x04
+
+#define OV5693_OFFSET_START_X_H_REG 0x3810
+#define OV5693_OFFSET_START_X_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_OFFSET_START_X_L_REG 0x3811
+#define OV5693_OFFSET_START_X_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_OFFSET_START_Y_H_REG 0x3812
+#define OV5693_OFFSET_START_Y_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_OFFSET_START_Y_L_REG 0x3813
+#define OV5693_OFFSET_START_Y_L(v) ((v) & GENMASK(7, 0))
+
+#define OV5693_SUB_INC_X_REG 0x3814
+#define OV5693_SUB_INC_Y_REG 0x3815
+
+#define OV5693_FORMAT1_REG 0x3820
+#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(2)
+#define OV5693_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1)
+#define OV5693_FORMAT1_VBIN_EN BIT(0)
+#define OV5693_FORMAT2_REG 0x3821
+#define OV5693_FORMAT2_HDR_EN BIT(7)
+#define OV5693_FORMAT2_FLIP_HORZ_ISP_EN BIT(2)
+#define OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1)
+#define OV5693_FORMAT2_HBIN_EN BIT(0)
+
+#define OV5693_ISP_CTRL2_REG 0x5002
+#define OV5693_ISP_SCALE_ENABLE BIT(7)
+
+/* Pixel Array */
+#define OV5693_NATIVE_WIDTH 2624
+#define OV5693_NATIVE_HEIGHT 1956
+#define OV5693_NATIVE_START_LEFT 0
+#define OV5693_NATIVE_START_TOP 0
+#define OV5693_ACTIVE_WIDTH 2592
+#define OV5693_ACTIVE_HEIGHT 1944
+#define OV5693_ACTIVE_START_LEFT 16
+#define OV5693_ACTIVE_START_TOP 6
+#define OV5693_MIN_CROP_WIDTH 2
+#define OV5693_MIN_CROP_HEIGHT 2
+
+/* Test Pattern */
+#define OV5693_TEST_PATTERN_REG 0x5e00
+#define OV5693_TEST_PATTERN_ENABLE BIT(7)
+#define OV5693_TEST_PATTERN_ROLLING BIT(6)
+#define OV5693_TEST_PATTERN_RANDOM 0x01
+#define OV5693_TEST_PATTERN_BARS 0x00
+
+/* System Frequencies */
+#define OV5693_XVCLK_FREQ 19200000
+#define OV5693_LINK_FREQ_400MHZ 400000000
+#define OV5693_PIXEL_RATE 160000000
+
+/* Miscellaneous */
+#define OV5693_NUM_SUPPLIES 2
+
+#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd)
+
+struct ov5693_reg {
+ u16 reg;
+ u8 val;
+};
+
+struct ov5693_reg_list {
+ u32 num_regs;
+ const struct ov5693_reg *regs;
+};
+
+struct ov5693_device {
+ struct i2c_client *client;
+ struct device *dev;
+
+ /* Protect against concurrent changes to controls */
+ struct mutex lock;
+
+ struct gpio_desc *reset;
+ struct gpio_desc *powerdown;
+ struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES];
+ struct clk *clk;
+
+ struct ov5693_mode {
+ struct v4l2_rect crop;
+ struct v4l2_mbus_framefmt format;
+ bool binning_x;
+ bool binning_y;
+ unsigned int inc_x_odd;
+ unsigned int inc_y_odd;
+ unsigned int vts;
+ } mode;
+ bool streaming;
+
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct ov5693_v4l2_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *analogue_gain;
+ struct v4l2_ctrl *digital_gain;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ } ctrls;
+};
+
+static const struct ov5693_reg ov5693_global_regs[] = {
+ {0x3016, 0xf0},
+ {0x3017, 0xf0},
+ {0x3018, 0xf0},
+ {0x3022, 0x01},
+ {0x3028, 0x44},
+ {0x3098, 0x02},
+ {0x3099, 0x19},
+ {0x309a, 0x02},
+ {0x309b, 0x01},
+ {0x309c, 0x00},
+ {0x30a0, 0xd2},
+ {0x30a2, 0x01},
+ {0x30b2, 0x00},
+ {0x30b3, 0x7d},
+ {0x30b4, 0x03},
+ {0x30b5, 0x04},
+ {0x30b6, 0x01},
+ {0x3104, 0x21},
+ {0x3106, 0x00},
+ {0x3406, 0x01},
+ {0x3503, 0x07},
+ {0x350b, 0x40},
+ {0x3601, 0x0a},
+ {0x3602, 0x38},
+ {0x3612, 0x80},
+ {0x3620, 0x54},
+ {0x3621, 0xc7},
+ {0x3622, 0x0f},
+ {0x3625, 0x10},
+ {0x3630, 0x55},
+ {0x3631, 0xf4},
+ {0x3632, 0x00},
+ {0x3633, 0x34},
+ {0x3634, 0x02},
+ {0x364d, 0x0d},
+ {0x364f, 0xdd},
+ {0x3660, 0x04},
+ {0x3662, 0x10},
+ {0x3663, 0xf1},
+ {0x3665, 0x00},
+ {0x3666, 0x20},
+ {0x3667, 0x00},
+ {0x366a, 0x80},
+ {0x3680, 0xe0},
+ {0x3681, 0x00},
+ {0x3700, 0x42},
+ {0x3701, 0x14},
+ {0x3702, 0xa0},
+ {0x3703, 0xd8},
+ {0x3704, 0x78},
+ {0x3705, 0x02},
+ {0x370a, 0x00},
+ {0x370b, 0x20},
+ {0x370c, 0x0c},
+ {0x370d, 0x11},
+ {0x370e, 0x00},
+ {0x370f, 0x40},
+ {0x3710, 0x00},
+ {0x371a, 0x1c},
+ {0x371b, 0x05},
+ {0x371c, 0x01},
+ {0x371e, 0xa1},
+ {0x371f, 0x0c},
+ {0x3721, 0x00},
+ {0x3724, 0x10},
+ {0x3726, 0x00},
+ {0x372a, 0x01},
+ {0x3730, 0x10},
+ {0x3738, 0x22},
+ {0x3739, 0xe5},
+ {0x373a, 0x50},
+ {0x373b, 0x02},
+ {0x373c, 0x41},
+ {0x373f, 0x02},
+ {0x3740, 0x42},
+ {0x3741, 0x02},
+ {0x3742, 0x18},
+ {0x3743, 0x01},
+ {0x3744, 0x02},
+ {0x3747, 0x10},
+ {0x374c, 0x04},
+ {0x3751, 0xf0},
+ {0x3752, 0x00},
+ {0x3753, 0x00},
+ {0x3754, 0xc0},
+ {0x3755, 0x00},
+ {0x3756, 0x1a},
+ {0x3758, 0x00},
+ {0x3759, 0x0f},
+ {0x376b, 0x44},
+ {0x375c, 0x04},
+ {0x3774, 0x10},
+ {0x3776, 0x00},
+ {0x377f, 0x08},
+ {0x3780, 0x22},
+ {0x3781, 0x0c},
+ {0x3784, 0x2c},
+ {0x3785, 0x1e},
+ {0x378f, 0xf5},
+ {0x3791, 0xb0},
+ {0x3795, 0x00},
+ {0x3796, 0x64},
+ {0x3797, 0x11},
+ {0x3798, 0x30},
+ {0x3799, 0x41},
+ {0x379a, 0x07},
+ {0x379b, 0xb0},
+ {0x379c, 0x0c},
+ {0x3a04, 0x06},
+ {0x3a05, 0x14},
+ {0x3e07, 0x20},
+ {0x4000, 0x08},
+ {0x4001, 0x04},
+ {0x4004, 0x08},
+ {0x4006, 0x20},
+ {0x4008, 0x24},
+ {0x4009, 0x10},
+ {0x4058, 0x00},
+ {0x4101, 0xb2},
+ {0x4307, 0x31},
+ {0x4511, 0x05},
+ {0x4512, 0x01},
+ {0x481f, 0x30},
+ {0x4826, 0x2c},
+ {0x4d02, 0xfd},
+ {0x4d03, 0xf5},
+ {0x4d04, 0x0c},
+ {0x4d05, 0xcc},
+ {0x4837, 0x0a},
+ {0x5003, 0x20},
+ {0x5013, 0x00},
+ {0x5842, 0x01},
+ {0x5843, 0x2b},
+ {0x5844, 0x01},
+ {0x5845, 0x92},
+ {0x5846, 0x01},
+ {0x5847, 0x8f},
+ {0x5848, 0x01},
+ {0x5849, 0x0c},
+ {0x5e10, 0x0c},
+ {0x3820, 0x00},
+ {0x3821, 0x1e},
+ {0x5041, 0x14}
+};
+
+static const struct ov5693_reg_list ov5693_global_setting = {
+ .num_regs = ARRAY_SIZE(ov5693_global_regs),
+ .regs = ov5693_global_regs,
+};
+
+static const struct v4l2_rect ov5693_default_crop = {
+ .left = OV5693_ACTIVE_START_LEFT,
+ .top = OV5693_ACTIVE_START_TOP,
+ .width = OV5693_ACTIVE_WIDTH,
+ .height = OV5693_ACTIVE_HEIGHT,
+};
+
+static const struct v4l2_mbus_framefmt ov5693_default_fmt = {
+ .width = OV5693_ACTIVE_WIDTH,
+ .height = OV5693_ACTIVE_HEIGHT,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+};
+
+static const s64 link_freq_menu_items[] = {
+ OV5693_LINK_FREQ_400MHZ
+};
+
+static const char * const ov5693_supply_names[] = {
+ "avdd",
+ "dovdd",
+};
+
+static const char * const ov5693_test_pattern_menu[] = {
+ "Disabled",
+ "Random Data",
+ "Colour Bars",
+ "Colour Bars with Rolling Bar"
+};
+
+static const u8 ov5693_test_pattern_bits[] = {
+ 0,
+ OV5693_TEST_PATTERN_ENABLE | OV5693_TEST_PATTERN_RANDOM,
+ OV5693_TEST_PATTERN_ENABLE | OV5693_TEST_PATTERN_BARS,
+ OV5693_TEST_PATTERN_ENABLE | OV5693_TEST_PATTERN_BARS |
+ OV5693_TEST_PATTERN_ROLLING,
+};
+
+/* I2C I/O Operations */
+
+static int ov5693_read_reg(struct ov5693_device *ov5693, u16 addr, u8 *value)
+{
+ struct i2c_client *client = ov5693->client;
+ struct i2c_msg msgs[2];
+ u8 addr_buf[2];
+ u8 data_buf;
+ int ret;
+
+ put_unaligned_be16(addr, addr_buf);
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = ARRAY_SIZE(addr_buf);
+ msgs[0].buf = addr_buf;
+
+ /* Read register value */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = &data_buf;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = data_buf;
+
+ return 0;
+}
+
+static void ov5693_write_reg(struct ov5693_device *ov5693, u16 addr, u8 value,
+ int *error)
+{
+ unsigned char data[3] = { addr >> 8, addr & 0xff, value };
+ int ret;
+
+ if (*error < 0)
+ return;
+
+ ret = i2c_master_send(ov5693->client, data, sizeof(data));
+ if (ret < 0) {
+ dev_dbg(ov5693->dev, "i2c send error at address 0x%04x: %d\n",
+ addr, ret);
+ *error = ret;
+ }
+}
+
+static int ov5693_write_reg_array(struct ov5693_device *ov5693,
+ const struct ov5693_reg_list *reglist)
+{
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < reglist->num_regs; i++)
+ ov5693_write_reg(ov5693, reglist->regs[i].reg,
+ reglist->regs[i].val, &ret);
+
+ return ret;
+}
+
+static int ov5693_update_bits(struct ov5693_device *ov5693, u16 address,
+ u16 mask, u16 bits)
+{
+ u8 value = 0;
+ int ret;
+
+ ret = ov5693_read_reg(ov5693, address, &value);
+ if (ret)
+ return ret;
+
+ value &= ~mask;
+ value |= bits;
+
+ ov5693_write_reg(ov5693, address, value, &ret);
+
+ return ret;
+}
+
+/* V4L2 Controls Functions */
+
+static int ov5693_flip_vert_configure(struct ov5693_device *ov5693, bool enable)
+{
+ u8 bits = OV5693_FORMAT1_FLIP_VERT_ISP_EN |
+ OV5693_FORMAT1_FLIP_VERT_SENSOR_EN;
+ int ret;
+
+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, bits,
+ enable ? bits : 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ov5693_flip_horz_configure(struct ov5693_device *ov5693, bool enable)
+{
+ u8 bits = OV5693_FORMAT2_FLIP_HORZ_ISP_EN |
+ OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN;
+ int ret;
+
+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, bits,
+ enable ? bits : 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ov5693_get_exposure(struct ov5693_device *ov5693, s32 *value)
+{
+ u8 exposure_hh = 0, exposure_h = 0, exposure_l = 0;
+ int ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_L_CTRL_HH_REG, &exposure_hh);
+ if (ret)
+ return ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_L_CTRL_H_REG, &exposure_h);
+ if (ret)
+ return ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_L_CTRL_L_REG, &exposure_l);
+ if (ret)
+ return ret;
+
+ /* The lowest 4 bits are unsupported fractional bits */
+ *value = ((exposure_hh << 16) | (exposure_h << 8) | exposure_l) >> 4;
+
+ return 0;
+}
+
+static int ov5693_exposure_configure(struct ov5693_device *ov5693, u32 exposure)
+{
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_EXPOSURE_L_CTRL_HH_REG,
+ OV5693_EXPOSURE_CTRL_HH(exposure), &ret);
+ ov5693_write_reg(ov5693, OV5693_EXPOSURE_L_CTRL_H_REG,
+ OV5693_EXPOSURE_CTRL_H(exposure), &ret);
+ ov5693_write_reg(ov5693, OV5693_EXPOSURE_L_CTRL_L_REG,
+ OV5693_EXPOSURE_CTRL_L(exposure), &ret);
+
+ return ret;
+}
+
+static int ov5693_get_gain(struct ov5693_device *ov5693, u32 *gain)
+{
+ u8 gain_l = 0, gain_h = 0;
+ int ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_GAIN_CTRL_H_REG, &gain_h);
+ if (ret)
+ return ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_GAIN_CTRL_L_REG, &gain_l);
+ if (ret)
+ return ret;
+
+ /* As with exposure, the lowest 4 bits are fractional bits. */
+ *gain = ((gain_h << 8) | gain_l) >> 4;
+
+ return ret;
+}
+
+static int ov5693_digital_gain_configure(struct ov5693_device *ov5693, u32 gain)
+{
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_MWB_RED_GAIN_H_REG,
+ OV5693_MWB_GAIN_H_CTRL(gain), &ret);
+ ov5693_write_reg(ov5693, OV5693_MWB_RED_GAIN_L_REG,
+ OV5693_MWB_GAIN_L_CTRL(gain), &ret);
+ ov5693_write_reg(ov5693, OV5693_MWB_GREEN_GAIN_H_REG,
+ OV5693_MWB_GAIN_H_CTRL(gain), &ret);
+ ov5693_write_reg(ov5693, OV5693_MWB_GREEN_GAIN_L_REG,
+ OV5693_MWB_GAIN_L_CTRL(gain), &ret);
+ ov5693_write_reg(ov5693, OV5693_MWB_BLUE_GAIN_H_REG,
+ OV5693_MWB_GAIN_H_CTRL(gain), &ret);
+ ov5693_write_reg(ov5693, OV5693_MWB_BLUE_GAIN_L_REG,
+ OV5693_MWB_GAIN_L_CTRL(gain), &ret);
+
+ return ret;
+}
+
+static int ov5693_analog_gain_configure(struct ov5693_device *ov5693, u32 gain)
+{
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_GAIN_CTRL_L_REG,
+ OV5693_GAIN_CTRL_L(gain), &ret);
+ ov5693_write_reg(ov5693, OV5693_GAIN_CTRL_H_REG,
+ OV5693_GAIN_CTRL_H(gain), &ret);
+
+ return ret;
+}
+
+static int ov5693_vts_configure(struct ov5693_device *ov5693, u32 vblank)
+{
+ u16 vts = ov5693->mode.format.height + vblank;
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_H_REG,
+ OV5693_TIMING_VTS_H(vts), &ret);
+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_L_REG,
+ OV5693_TIMING_VTS_L(vts), &ret);
+
+ return ret;
+}
+
+static int ov5693_test_pattern_configure(struct ov5693_device *ov5693, u32 idx)
+{
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_TEST_PATTERN_REG,
+ ov5693_test_pattern_bits[idx], &ret);
+
+ return ret;
+}
+
+static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5693_device *ov5693 =
+ container_of(ctrl->handler, struct ov5693_device, ctrls.handler);
+ int ret = 0;
+
+ /* If VBLANK is altered we need to update exposure to compensate */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ int exposure_max;
+
+ exposure_max = ov5693->mode.format.height + ctrl->val -
+ OV5693_INTEGRATION_TIME_MARGIN;
+ __v4l2_ctrl_modify_range(ov5693->ctrls.exposure,
+ ov5693->ctrls.exposure->minimum,
+ exposure_max,
+ ov5693->ctrls.exposure->step,
+ min(ov5693->ctrls.exposure->val, exposure_max));
+ }
+
+ /* Only apply changes to the controls if the device is powered up */
+ if (!pm_runtime_get_if_in_use(ov5693->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = ov5693_exposure_configure(ov5693, ctrl->val);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov5693_analog_gain_configure(ov5693, ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = ov5693_digital_gain_configure(ov5693, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ ret = ov5693_flip_horz_configure(ov5693, !!ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = ov5693_flip_vert_configure(ov5693, !!ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = ov5693_vts_configure(ov5693, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5693_test_pattern_configure(ov5693, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ pm_runtime_put(ov5693->dev);
+
+ return ret;
+}
+
+static int ov5693_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5693_device *ov5693 =
+ container_of(ctrl->handler, struct ov5693_device, ctrls.handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_ABSOLUTE:
+ return ov5693_get_exposure(ov5693, &ctrl->val);
+ case V4L2_CID_AUTOGAIN:
+ return ov5693_get_gain(ov5693, &ctrl->val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops ov5693_ctrl_ops = {
+ .s_ctrl = ov5693_s_ctrl,
+ .g_volatile_ctrl = ov5693_g_volatile_ctrl
+};
+
+/* System Control Functions */
+
+static int ov5693_mode_configure(struct ov5693_device *ov5693)
+{
+ const struct ov5693_mode *mode = &ov5693->mode;
+ int ret = 0;
+
+ /* Crop Start X */
+ ov5693_write_reg(ov5693, OV5693_CROP_START_X_H_REG,
+ OV5693_CROP_START_X_H(mode->crop.left), &ret);
+ ov5693_write_reg(ov5693, OV5693_CROP_START_X_L_REG,
+ OV5693_CROP_START_X_L(mode->crop.left), &ret);
+
+ /* Offset X */
+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_X_H_REG,
+ OV5693_OFFSET_START_X_H(0), &ret);
+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_X_L_REG,
+ OV5693_OFFSET_START_X_L(0), &ret);
+
+ /* Output Size X */
+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_X_H_REG,
+ OV5693_OUTPUT_SIZE_X_H(mode->format.width), &ret);
+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_X_L_REG,
+ OV5693_OUTPUT_SIZE_X_L(mode->format.width), &ret);
+
+ /* Crop End X */
+ ov5693_write_reg(ov5693, OV5693_CROP_END_X_H_REG,
+ OV5693_CROP_END_X_H(mode->crop.left + mode->crop.width),
+ &ret);
+ ov5693_write_reg(ov5693, OV5693_CROP_END_X_L_REG,
+ OV5693_CROP_END_X_L(mode->crop.left + mode->crop.width),
+ &ret);
+
+ /* Horizontal Total Size */
+ ov5693_write_reg(ov5693, OV5693_TIMING_HTS_H_REG,
+ OV5693_TIMING_HTS_H(OV5693_FIXED_PPL), &ret);
+ ov5693_write_reg(ov5693, OV5693_TIMING_HTS_L_REG,
+ OV5693_TIMING_HTS_L(OV5693_FIXED_PPL), &ret);
+
+ /* Crop Start Y */
+ ov5693_write_reg(ov5693, OV5693_CROP_START_Y_H_REG,
+ OV5693_CROP_START_Y_H(mode->crop.top), &ret);
+ ov5693_write_reg(ov5693, OV5693_CROP_START_Y_L_REG,
+ OV5693_CROP_START_Y_L(mode->crop.top), &ret);
+
+ /* Offset Y */
+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_Y_H_REG,
+ OV5693_OFFSET_START_Y_H(0), &ret);
+ ov5693_write_reg(ov5693, OV5693_OFFSET_START_Y_L_REG,
+ OV5693_OFFSET_START_Y_L(0), &ret);
+
+ /* Output Size Y */
+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_Y_H_REG,
+ OV5693_OUTPUT_SIZE_Y_H(mode->format.height), &ret);
+ ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_Y_L_REG,
+ OV5693_OUTPUT_SIZE_Y_L(mode->format.height), &ret);
+
+ /* Crop End Y */
+ ov5693_write_reg(ov5693, OV5693_CROP_END_Y_H_REG,
+ OV5693_CROP_END_Y_H(mode->crop.top + mode->crop.height),
+ &ret);
+ ov5693_write_reg(ov5693, OV5693_CROP_END_Y_L_REG,
+ OV5693_CROP_END_Y_L(mode->crop.top + mode->crop.height),
+ &ret);
+
+ /* Vertical Total Size */
+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_H_REG,
+ OV5693_TIMING_VTS_H(mode->vts), &ret);
+ ov5693_write_reg(ov5693, OV5693_TIMING_VTS_L_REG,
+ OV5693_TIMING_VTS_L(mode->vts), &ret);
+
+ /* Subsample X increase */
+ ov5693_write_reg(ov5693, OV5693_SUB_INC_X_REG,
+ ((mode->inc_x_odd << 4) & 0xf0) | 0x01, &ret);
+ /* Subsample Y increase */
+ ov5693_write_reg(ov5693, OV5693_SUB_INC_Y_REG,
+ ((mode->inc_y_odd << 4) & 0xf0) | 0x01, &ret);
+
+ /* Binning */
+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG,
+ OV5693_FORMAT1_VBIN_EN,
+ mode->binning_y ? OV5693_FORMAT1_VBIN_EN : 0);
+ if (ret)
+ return ret;
+
+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG,
+ OV5693_FORMAT2_HBIN_EN,
+ mode->binning_x ? OV5693_FORMAT2_HBIN_EN : 0);
+
+ return ret;
+}
+
+static int ov5693_sw_standby(struct ov5693_device *ov5693, bool standby)
+{
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_SW_STREAM_REG,
+ standby ? OV5693_STOP_STREAMING : OV5693_START_STREAMING,
+ &ret);
+
+ return ret;
+}
+
+static int ov5693_sw_reset(struct ov5693_device *ov5693)
+{
+ int ret = 0;
+
+ ov5693_write_reg(ov5693, OV5693_SW_RESET_REG, OV5693_SW_RESET, &ret);
+
+ return ret;
+}
+
+static int ov5693_sensor_init(struct ov5693_device *ov5693)
+{
+ int ret = 0;
+
+ ret = ov5693_sw_reset(ov5693);
+ if (ret) {
+ dev_err(ov5693->dev, "%s software reset error\n", __func__);
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(ov5693, &ov5693_global_setting);
+ if (ret) {
+ dev_err(ov5693->dev, "%s global settings error\n", __func__);
+ return ret;
+ }
+
+ ret = ov5693_mode_configure(ov5693);
+ if (ret) {
+ dev_err(ov5693->dev, "%s mode configure error\n", __func__);
+ return ret;
+ }
+
+ ret = ov5693_sw_standby(ov5693, true);
+ if (ret)
+ dev_err(ov5693->dev, "%s software standby error\n", __func__);
+
+ return ret;
+}
+
+static void ov5693_sensor_powerdown(struct ov5693_device *ov5693)
+{
+ gpiod_set_value_cansleep(ov5693->reset, 1);
+ gpiod_set_value_cansleep(ov5693->powerdown, 1);
+
+ regulator_bulk_disable(OV5693_NUM_SUPPLIES, ov5693->supplies);
+
+ clk_disable_unprepare(ov5693->clk);
+}
+
+static int ov5693_sensor_powerup(struct ov5693_device *ov5693)
+{
+ int ret;
+
+ gpiod_set_value_cansleep(ov5693->reset, 1);
+ gpiod_set_value_cansleep(ov5693->powerdown, 1);
+
+ ret = clk_prepare_enable(ov5693->clk);
+ if (ret) {
+ dev_err(ov5693->dev, "Failed to enable clk\n");
+ goto fail_power;
+ }
+
+ ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES, ov5693->supplies);
+ if (ret) {
+ dev_err(ov5693->dev, "Failed to enable regulators\n");
+ goto fail_power;
+ }
+
+ gpiod_set_value_cansleep(ov5693->powerdown, 0);
+ gpiod_set_value_cansleep(ov5693->reset, 0);
+
+ usleep_range(5000, 7500);
+
+ return 0;
+
+fail_power:
+ ov5693_sensor_powerdown(ov5693);
+ return ret;
+}
+
+static int __maybe_unused ov5693_sensor_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+
+ ov5693_sensor_powerdown(ov5693);
+
+ return 0;
+}
+
+static int __maybe_unused ov5693_sensor_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ int ret;
+
+ mutex_lock(&ov5693->lock);
+
+ ret = ov5693_sensor_powerup(ov5693);
+ if (ret)
+ goto out_unlock;
+
+ ret = ov5693_sensor_init(ov5693);
+ if (ret) {
+ dev_err(dev, "ov5693 sensor init failure\n");
+ goto err_power;
+ }
+
+ goto out_unlock;
+
+err_power:
+ ov5693_sensor_powerdown(ov5693);
+out_unlock:
+ mutex_unlock(&ov5693->lock);
+ return ret;
+}
+
+static int ov5693_detect(struct ov5693_device *ov5693)
+{
+ u8 id_l = 0, id_h = 0;
+ u16 id = 0;
+ int ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_REG_CHIP_ID_H, &id_h);
+ if (ret)
+ return ret;
+
+ ret = ov5693_read_reg(ov5693, OV5693_REG_CHIP_ID_L, &id_l);
+ if (ret)
+ return ret;
+
+ id = (id_h << 8) | id_l;
+
+ if (id != OV5693_CHIP_ID) {
+ dev_err(ov5693->dev, "sensor ID mismatch. Found 0x%04x\n", id);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* V4L2 Framework callbacks */
+
+static unsigned int __ov5693_calc_vts(u32 height)
+{
+ /*
+ * We need to set a sensible default VTS for whatever format height we
+ * happen to be given from set_fmt(). This function just targets
+ * an even multiple of 30fps.
+ */
+
+ unsigned int tgt_fps;
+
+ tgt_fps = rounddown(OV5693_PIXEL_RATE / OV5693_FIXED_PPL / height, 30);
+
+ return ALIGN_DOWN(OV5693_PIXEL_RATE / OV5693_FIXED_PPL / tgt_fps, 2);
+}
+
+static struct v4l2_mbus_framefmt *
+__ov5693_get_pad_format(struct ov5693_device *ov5693,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&ov5693->sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &ov5693->mode.format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__ov5693_get_pad_crop(struct ov5693_device *ov5693,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(&ov5693->sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &ov5693->mode.crop;
+ }
+
+ return NULL;
+}
+
+static int ov5693_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+
+ format->format = ov5693->mode.format;
+
+ return 0;
+}
+
+static int ov5693_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ const struct v4l2_rect *crop;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int hratio, vratio;
+ unsigned int width, height;
+ unsigned int hblank;
+ int exposure_max;
+ int ret = 0;
+
+ crop = __ov5693_get_pad_crop(ov5693, cfg, format->pad, format->which);
+
+ /*
+ * Align to two to simplify the binning calculations below, and clamp
+ * the requested format at the crop rectangle
+ */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ OV5693_MIN_CROP_WIDTH, crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ OV5693_MIN_CROP_HEIGHT, crop->height);
+
+ /*
+ * We can only support setting either the dimensions of the crop rect
+ * or those dimensions binned (separately) by a factor of two.
+ */
+ hratio = clamp_t(unsigned int, DIV_ROUND_CLOSEST(crop->width, width), 1, 2);
+ vratio = clamp_t(unsigned int, DIV_ROUND_CLOSEST(crop->height, height), 1, 2);
+
+ fmt = __ov5693_get_pad_format(ov5693, cfg, format->pad, format->which);
+
+ fmt->width = crop->width / hratio;
+ fmt->height = crop->height / vratio;
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ format->format = *fmt;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return ret;
+
+ mutex_lock(&ov5693->lock);
+
+ ov5693->mode.binning_x = hratio > 1 ? true : false;
+ ov5693->mode.inc_x_odd = hratio > 1 ? 3 : 1;
+ ov5693->mode.binning_y = vratio > 1 ? true : false;
+ ov5693->mode.inc_y_odd = vratio > 1 ? 3 : 1;
+
+ ov5693->mode.vts = __ov5693_calc_vts(fmt->height);
+
+ __v4l2_ctrl_modify_range(ov5693->ctrls.vblank,
+ OV5693_TIMING_MIN_VTS,
+ OV5693_TIMING_MAX_VTS - fmt->height,
+ 1, ov5693->mode.vts - fmt->height);
+ __v4l2_ctrl_s_ctrl(ov5693->ctrls.vblank,
+ ov5693->mode.vts - fmt->height);
+
+ hblank = OV5693_FIXED_PPL - fmt->width;
+ __v4l2_ctrl_modify_range(ov5693->ctrls.hblank, hblank, hblank, 1,
+ hblank);
+
+ exposure_max = ov5693->mode.vts - OV5693_INTEGRATION_TIME_MARGIN;
+ __v4l2_ctrl_modify_range(ov5693->ctrls.exposure,
+ ov5693->ctrls.exposure->minimum, exposure_max,
+ ov5693->ctrls.exposure->step,
+ min(ov5693->ctrls.exposure->val, exposure_max));
+
+ mutex_unlock(&ov5693->lock);
+ return ret;
+}
+
+static int ov5693_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ mutex_lock(&ov5693->lock);
+ sel->r = *__ov5693_get_pad_crop(ov5693, cfg, sel->pad,
+ sel->which);
+ mutex_unlock(&ov5693->lock);
+ break;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = OV5693_NATIVE_WIDTH;
+ sel->r.height = OV5693_NATIVE_HEIGHT;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.top = OV5693_ACTIVE_START_TOP;
+ sel->r.left = OV5693_ACTIVE_START_LEFT;
+ sel->r.width = OV5693_ACTIVE_WIDTH;
+ sel->r.height = OV5693_ACTIVE_HEIGHT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ov5693_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ /*
+ * Clamp the boundaries of the crop rectangle to the size of the sensor
+ * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't
+ * disrupted.
+ */
+ rect.left = clamp(ALIGN(sel->r.left, 2), OV5693_NATIVE_START_LEFT,
+ OV5693_NATIVE_WIDTH);
+ rect.top = clamp(ALIGN(sel->r.top, 2), OV5693_NATIVE_START_TOP,
+ OV5693_NATIVE_HEIGHT);
+ rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2),
+ OV5693_MIN_CROP_WIDTH, OV5693_NATIVE_WIDTH);
+ rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2),
+ OV5693_MIN_CROP_HEIGHT, OV5693_NATIVE_HEIGHT);
+
+ /* Make sure the crop rectangle isn't outside the bounds of the array */
+ rect.width = min_t(unsigned int, rect.width,
+ OV5693_NATIVE_WIDTH - rect.left);
+ rect.height = min_t(unsigned int, rect.height,
+ OV5693_NATIVE_HEIGHT - rect.top);
+
+ __crop = __ov5693_get_pad_crop(ov5693, cfg, sel->pad, sel->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /*
+ * Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = __ov5693_get_pad_format(ov5693, cfg, sel->pad, sel->which);
+ format->width = rect.width;
+ format->height = rect.height;
+ }
+
+ *__crop = rect;
+ sel->r = rect;
+
+ return 0;
+}
+
+static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ int ret;
+
+ if (enable) {
+ ret = pm_runtime_get_sync(ov5693->dev);
+ if (ret < 0)
+ goto err_power_down;
+
+ ret = __v4l2_ctrl_handler_setup(&ov5693->ctrls.handler);
+ if (ret)
+ goto err_power_down;
+ }
+
+ mutex_lock(&ov5693->lock);
+ ret = ov5693_sw_standby(ov5693, !enable);
+ mutex_unlock(&ov5693->lock);
+
+ if (ret)
+ goto err_power_down;
+ ov5693->streaming = !!enable;
+
+ if (!enable)
+ pm_runtime_put(ov5693->dev);
+
+ return 0;
+err_power_down:
+ pm_runtime_put_noidle(ov5693->dev);
+ return ret;
+}
+
+static int ov5693_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ unsigned int framesize = OV5693_FIXED_PPL * (ov5693->mode.format.height +
+ ov5693->ctrls.vblank->val);
+ unsigned int fps = DIV_ROUND_CLOSEST(OV5693_PIXEL_RATE, framesize);
+
+ interval->interval.numerator = 1;
+ interval->interval.denominator = fps;
+
+ return 0;
+}
+
+static int ov5693_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* Only a single mbus format is supported */
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
+static int ov5693_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ struct v4l2_rect *__crop;
+
+ if (fse->index > 1 || fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ return -EINVAL;
+
+ __crop = __ov5693_get_pad_crop(ov5693, cfg, fse->pad, fse->which);
+ if (!__crop)
+ return -EINVAL;
+
+ fse->min_width = __crop->width / (fse->index + 1);
+ fse->min_height = __crop->height / (fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov5693_video_ops = {
+ .s_stream = ov5693_s_stream,
+ .g_frame_interval = ov5693_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops ov5693_pad_ops = {
+ .enum_mbus_code = ov5693_enum_mbus_code,
+ .enum_frame_size = ov5693_enum_frame_size,
+ .get_fmt = ov5693_get_fmt,
+ .set_fmt = ov5693_set_fmt,
+ .get_selection = ov5693_get_selection,
+ .set_selection = ov5693_set_selection,
+};
+
+static const struct v4l2_subdev_ops ov5693_ops = {
+ .video = &ov5693_video_ops,
+ .pad = &ov5693_pad_ops,
+};
+
+/* Sensor and Driver Configuration Functions */
+
+static int ov5693_init_controls(struct ov5693_device *ov5693)
+{
+ const struct v4l2_ctrl_ops *ops = &ov5693_ctrl_ops;
+ struct v4l2_fwnode_device_properties props;
+ int vblank_max, vblank_def;
+ int exposure_max;
+ int hblank;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&ov5693->ctrls.handler, 12);
+ if (ret)
+ return ret;
+
+ /* link freq */
+ ov5693->ctrls.link_freq = v4l2_ctrl_new_int_menu(&ov5693->ctrls.handler,
+ NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ov5693->ctrls.link_freq)
+ ov5693->ctrls.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* pixel rate */
+ ov5693->ctrls.pixel_rate = v4l2_ctrl_new_std(&ov5693->ctrls.handler, NULL,
+ V4L2_CID_PIXEL_RATE, 0,
+ OV5693_PIXEL_RATE, 1,
+ OV5693_PIXEL_RATE);
+
+ /* Exposure */
+ exposure_max = ov5693->mode.vts - OV5693_INTEGRATION_TIME_MARGIN;
+ ov5693->ctrls.exposure = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops,
+ V4L2_CID_EXPOSURE,
+ OV5693_EXPOSURE_MIN,
+ exposure_max,
+ OV5693_EXPOSURE_STEP,
+ exposure_max);
+
+ /* Gain */
+ ov5693->ctrls.analogue_gain = v4l2_ctrl_new_std(&ov5693->ctrls.handler,
+ ops, V4L2_CID_ANALOGUE_GAIN,
+ OV5693_GAIN_MIN,
+ OV5693_GAIN_MAX,
+ OV5693_GAIN_STEP,
+ OV5693_GAIN_DEF);
+
+ ov5693->ctrls.digital_gain = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops,
+ V4L2_CID_DIGITAL_GAIN,
+ OV5693_DIGITAL_GAIN_MIN,
+ OV5693_DIGITAL_GAIN_MAX,
+ OV5693_DIGITAL_GAIN_STEP,
+ OV5693_DIGITAL_GAIN_DEF);
+
+ /* Flip */
+ ov5693->ctrls.hflip = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ ov5693->ctrls.vflip = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ hblank = OV5693_FIXED_PPL - ov5693->mode.format.width;
+ ov5693->ctrls.hblank = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops,
+ V4L2_CID_HBLANK, hblank,
+ hblank, 1, hblank);
+
+ if (ov5693->ctrls.hblank)
+ ov5693->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_max = OV5693_TIMING_MAX_VTS - ov5693->mode.format.height;
+ vblank_def = ov5693->mode.vts - ov5693->mode.format.height;
+ ov5693->ctrls.vblank = v4l2_ctrl_new_std(&ov5693->ctrls.handler, ops,
+ V4L2_CID_VBLANK,
+ OV5693_TIMING_MIN_VTS,
+ vblank_max, 1, vblank_def);
+
+ ov5693->ctrls.test_pattern = v4l2_ctrl_new_std_menu_items(
+ &ov5693->ctrls.handler, ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov5693_test_pattern_menu) - 1,
+ 0, 0, ov5693_test_pattern_menu);
+
+ if (ov5693->ctrls.handler.error) {
+ dev_err(ov5693->dev, "Error initialising v4l2 ctrls\n");
+ ret = ov5693->ctrls.handler.error;
+ goto err_free_handler;
+ }
+
+ /* set properties from fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(ov5693->dev, &props);
+ if (ret)
+ goto err_free_handler;
+
+ ret = v4l2_ctrl_new_fwnode_properties(&ov5693->ctrls.handler, ops,
+ &props);
+ if (ret)
+ goto err_free_handler;
+
+ /* Use same lock for controls as for everything else. */
+ ov5693->ctrls.handler.lock = &ov5693->lock;
+ ov5693->sd.ctrl_handler = &ov5693->ctrls.handler;
+
+ return 0;
+
+err_free_handler:
+ v4l2_ctrl_handler_free(&ov5693->ctrls.handler);
+ return ret;
+}
+
+static int ov5693_configure_gpios(struct ov5693_device *ov5693)
+{
+ ov5693->reset = devm_gpiod_get_optional(ov5693->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5693->reset)) {
+ dev_err(ov5693->dev, "Error fetching reset GPIO\n");
+ return PTR_ERR(ov5693->reset);
+ }
+
+ ov5693->powerdown = devm_gpiod_get_optional(ov5693->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5693->powerdown)) {
+ dev_err(ov5693->dev, "Error fetching powerdown GPIO\n");
+ return PTR_ERR(ov5693->powerdown);
+ }
+
+ return 0;
+}
+
+static int ov5693_get_regulators(struct ov5693_device *ov5693)
+{
+ unsigned int i;
+
+ for (i = 0; i < OV5693_NUM_SUPPLIES; i++)
+ ov5693->supplies[i].supply = ov5693_supply_names[i];
+
+ return devm_regulator_bulk_get(ov5693->dev, OV5693_NUM_SUPPLIES,
+ ov5693->supplies);
+}
+
+static int ov5693_probe(struct i2c_client *client)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(&client->dev);
+ struct fwnode_handle *endpoint;
+ struct ov5693_device *ov5693;
+ u32 clk_rate;
+ int ret = 0;
+
+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!endpoint && !IS_ERR_OR_NULL(fwnode->secondary))
+ endpoint = fwnode_graph_get_next_endpoint(fwnode->secondary, NULL);
+ if (!endpoint)
+ return -EPROBE_DEFER;
+
+ ov5693 = devm_kzalloc(&client->dev, sizeof(*ov5693), GFP_KERNEL);
+ if (!ov5693)
+ return -ENOMEM;
+
+ ov5693->client = client;
+ ov5693->dev = &client->dev;
+
+ mutex_init(&ov5693->lock);
+
+ v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops);
+
+ ov5693->clk = devm_clk_get(&client->dev, "xvclk");
+ if (IS_ERR(ov5693->clk)) {
+ dev_err(&client->dev, "Error getting clock\n");
+ return PTR_ERR(ov5693->clk);
+ }
+
+ clk_rate = clk_get_rate(ov5693->clk);
+ if (clk_rate != OV5693_XVCLK_FREQ) {
+ dev_err(&client->dev, "Unsupported clk freq %u, expected %u\n",
+ clk_rate, OV5693_XVCLK_FREQ);
+ return -EINVAL;
+ }
+
+ ret = ov5693_configure_gpios(ov5693);
+ if (ret)
+ return ret;
+
+ ret = ov5693_get_regulators(ov5693);
+ if (ret) {
+ dev_err(&client->dev, "Error fetching regulators\n");
+ return ret;
+ }
+
+ ov5693->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov5693->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov5693->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ov5693->mode.crop = ov5693_default_crop;
+ ov5693->mode.format = ov5693_default_fmt;
+ ov5693->mode.vts = __ov5693_calc_vts(ov5693->mode.format.height);
+
+ ret = ov5693_init_controls(ov5693);
+ if (ret)
+ return ret;
+
+ ret = media_entity_pads_init(&ov5693->sd.entity, 1, &ov5693->pad);
+ if (ret)
+ goto err_ctrl_handler_free;
+
+ /*
+ * 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 streaming
+ * will work.
+ */
+
+ ret = ov5693_sensor_powerup(ov5693);
+ if (ret)
+ goto err_media_entity_cleanup;
+
+ ret = ov5693_detect(ov5693);
+ 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_sensor(&ov5693->sd);
+ if (ret) {
+ dev_err(&client->dev, "failed to register V4L2 subdev: %d",
+ ret);
+ 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:
+ ov5693_sensor_powerdown(ov5693);
+err_media_entity_cleanup:
+ media_entity_cleanup(&ov5693->sd.entity);
+err_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&ov5693->ctrls.handler);
+
+ return ret;
+}
+
+static int ov5693_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&ov5693->sd.entity);
+ v4l2_ctrl_handler_free(&ov5693->ctrls.handler);
+ mutex_destroy(&ov5693->lock);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ ov5693_sensor_powerdown(ov5693);
+ pm_runtime_set_suspended(&client->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov5693_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov5693_sensor_suspend, ov5693_sensor_resume, NULL)
+};
+
+static const struct acpi_device_id ov5693_acpi_match[] = {
+ {"INT33BE"},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match);
+
+static struct i2c_driver ov5693_driver = {
+ .driver = {
+ .name = "ov5693",
+ .acpi_match_table = ov5693_acpi_match,
+ .pm = &ov5693_pm_ops,
+ },
+ .probe_new = ov5693_probe,
+ .remove = ov5693_remove,
+};
+module_i2c_driver(ov5693_driver);
+
+MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors");
+MODULE_LICENSE("GPL");
--
2.33.0
From bbaa83538fbcfaf3d26a03019a2fad4ffe6243d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabian=20W=C3=BCthrich?= <me@fabwu.ch>
Date: Fri, 22 Jan 2021 20:58:13 +0100
Subject: [PATCH] cio2-bridge: Parse sensor orientation and rotation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The sensor orientation is read from the _PLC ACPI buffer and converted
to a v4l2 format.
See https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf
page 351 for a definition of the Panel property.
The sensor rotation is read from the SSDB ACPI buffer and converted into
degrees.
Signed-off-by: Fabian Wüthrich <me@fabwu.ch>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/cio2-bridge.c | 45 ++++++++++++++++++++--
drivers/media/pci/intel/ipu3/cio2-bridge.h | 3 ++
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
index 59a36f922675..d4a48cec2644 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.c
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -29,6 +29,7 @@ static const struct cio2_sensor_config cio2_supported_sensors[] = {
static const struct cio2_property_names prop_names = {
.clock_frequency = "clock-frequency",
.rotation = "rotation",
+ .orientation = "orientation",
.bus_type = "bus-type",
.data_lanes = "data-lanes",
.remote_endpoint = "remote-endpoint",
@@ -72,11 +73,36 @@ static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
return ret;
}
+static u32 cio2_bridge_parse_rotation(u8 rotation)
+{
+ if (rotation == 1)
+ return 180;
+ return 0;
+}
+
+static enum v4l2_fwnode_orientation cio2_bridge_parse_orientation(u8 panel)
+{
+ switch (panel) {
+ case 4:
+ return V4L2_FWNODE_ORIENTATION_FRONT;
+ case 5:
+ return V4L2_FWNODE_ORIENTATION_BACK;
+ default:
+ return V4L2_FWNODE_ORIENTATION_EXTERNAL;
+ }
+}
+
static void cio2_bridge_create_fwnode_properties(
struct cio2_sensor *sensor,
struct cio2_bridge *bridge,
const struct cio2_sensor_config *cfg)
{
+ u32 rotation;
+ enum v4l2_fwnode_orientation orientation;
+
+ rotation = cio2_bridge_parse_rotation(sensor->ssdb.degree);
+ orientation = cio2_bridge_parse_orientation(sensor->pld->panel);
+
sensor->prop_names = prop_names;
sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CIO2_ENDPOINT]);
@@ -85,9 +111,12 @@ static void cio2_bridge_create_fwnode_properties(
sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
sensor->prop_names.clock_frequency,
sensor->ssdb.mclkspeed);
- sensor->dev_properties[1] = PROPERTY_ENTRY_U8(
+ sensor->dev_properties[1] = PROPERTY_ENTRY_U32(
sensor->prop_names.rotation,
- sensor->ssdb.degree);
+ rotation);
+ sensor->dev_properties[2] = PROPERTY_ENTRY_U32(
+ sensor->prop_names.orientation,
+ orientation);
sensor->ep_properties[0] = PROPERTY_ENTRY_U32(
sensor->prop_names.bus_type,
@@ -159,6 +188,7 @@ static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge)
for (i = 0; i < bridge->n_sensors; i++) {
sensor = &bridge->sensors[i];
software_node_unregister_nodes(sensor->swnodes);
+ ACPI_FREE(sensor->pld);
acpi_dev_put(sensor->adev);
}
}
@@ -170,6 +200,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
struct fwnode_handle *fwnode;
struct cio2_sensor *sensor;
struct acpi_device *adev;
+ acpi_status status;
int ret;
for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
@@ -191,11 +222,15 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
if (ret)
goto err_put_adev;
+ status = acpi_get_physical_device_location(adev->handle, &sensor->pld);
+ if (ACPI_FAILURE(status))
+ goto err_put_adev;
+
if (sensor->ssdb.lanes > CIO2_MAX_LANES) {
dev_err(&adev->dev,
"Number of lanes in SSDB is invalid\n");
ret = -EINVAL;
- goto err_put_adev;
+ goto err_free_pld;
}
cio2_bridge_create_fwnode_properties(sensor, bridge, cfg);
@@ -203,7 +238,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
ret = software_node_register_nodes(sensor->swnodes);
if (ret)
- goto err_put_adev;
+ goto err_free_pld;
fwnode = software_node_fwnode(&sensor->swnodes[
SWNODE_SENSOR_HID]);
@@ -225,6 +260,8 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
err_free_swnodes:
software_node_unregister_nodes(sensor->swnodes);
+err_free_pld:
+ ACPI_FREE(sensor->pld);
err_put_adev:
acpi_dev_put(sensor->adev);
return ret;
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
index dd0ffcafa489..924d99d20328 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.h
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -80,6 +80,7 @@ struct cio2_sensor_ssdb {
struct cio2_property_names {
char clock_frequency[16];
char rotation[9];
+ char orientation[12];
char bus_type[9];
char data_lanes[11];
char remote_endpoint[16];
@@ -106,6 +107,8 @@ struct cio2_sensor {
struct cio2_node_names node_names;
struct cio2_sensor_ssdb ssdb;
+ struct acpi_pld_info *pld;
+
struct cio2_property_names prop_names;
struct property_entry ep_properties[5];
struct property_entry dev_properties[3];
--
2.33.0
From 2daaab808ea446cbfee2022b1b94ac20cbeaf466 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabian=20W=C3=BCthrich?= <me@fabwu.ch>
Date: Sun, 24 Jan 2021 11:07:42 +0100
Subject: [PATCH] cio2-bridge: Use macros and add warnings
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use macros for the _PLD panel as defined in the ACPI spec 6.3 and emit
a warning if we see an unknown value.
Signed-off-by: Fabian Wüthrich <me@fabwu.ch>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/cio2-bridge.c | 33 ++++++++++++++++------
drivers/media/pci/intel/ipu3/cio2-bridge.h | 13 +++++++++
2 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
index d4a48cec2644..1052885caa25 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.c
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -73,21 +73,36 @@ static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
return ret;
}
-static u32 cio2_bridge_parse_rotation(u8 rotation)
+static u32 cio2_bridge_parse_rotation(struct cio2_sensor *sensor)
{
- if (rotation == 1)
+ switch (sensor->ssdb.degree) {
+ case CIO2_SENSOR_ROTATION_NORMAL:
+ return 0;
+ case CIO2_SENSOR_ROTATION_INVERTED:
return 180;
- return 0;
+ default:
+ dev_warn(&sensor->adev->dev,
+ "Unknown rotation %d. Assume 0 degree rotation\n",
+ sensor->ssdb.degree);
+ return 0;
+ }
}
-static enum v4l2_fwnode_orientation cio2_bridge_parse_orientation(u8 panel)
+static enum v4l2_fwnode_orientation cio2_bridge_parse_orientation(struct cio2_sensor *sensor)
{
- switch (panel) {
- case 4:
+ switch (sensor->pld->panel) {
+ case CIO2_PLD_PANEL_FRONT:
return V4L2_FWNODE_ORIENTATION_FRONT;
- case 5:
+ case CIO2_PLD_PANEL_BACK:
return V4L2_FWNODE_ORIENTATION_BACK;
+ case CIO2_PLD_PANEL_TOP:
+ case CIO2_PLD_PANEL_LEFT:
+ case CIO2_PLD_PANEL_RIGHT:
+ case CIO2_PLD_PANEL_UNKNOWN:
+ return V4L2_FWNODE_ORIENTATION_EXTERNAL;
default:
+ dev_warn(&sensor->adev->dev, "Unknown _PLD panel value %d\n",
+ sensor->pld->panel);
return V4L2_FWNODE_ORIENTATION_EXTERNAL;
}
}
@@ -100,8 +115,8 @@ static void cio2_bridge_create_fwnode_properties(
u32 rotation;
enum v4l2_fwnode_orientation orientation;
- rotation = cio2_bridge_parse_rotation(sensor->ssdb.degree);
- orientation = cio2_bridge_parse_orientation(sensor->pld->panel);
+ rotation = cio2_bridge_parse_rotation(sensor);
+ orientation = cio2_bridge_parse_orientation(sensor);
sensor->prop_names = prop_names;
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
index 924d99d20328..e1e388cc9f45 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.h
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -12,6 +12,19 @@
#define CIO2_MAX_LANES 4
#define MAX_NUM_LINK_FREQS 3
+/* Values are estimated guesses as we don't have a spec */
+#define CIO2_SENSOR_ROTATION_NORMAL 0
+#define CIO2_SENSOR_ROTATION_INVERTED 1
+
+/* Panel position defined in _PLD section of ACPI Specification 6.3 */
+#define CIO2_PLD_PANEL_TOP 0
+#define CIO2_PLD_PANEL_BOTTOM 1
+#define CIO2_PLD_PANEL_LEFT 2
+#define CIO2_PLD_PANEL_RIGHT 3
+#define CIO2_PLD_PANEL_FRONT 4
+#define CIO2_PLD_PANEL_BACK 5
+#define CIO2_PLD_PANEL_UNKNOWN 6
+
#define CIO2_SENSOR_CONFIG(_HID, _NR, ...) \
(const struct cio2_sensor_config) { \
.hid = _HID, \
--
2.33.0
From c5d21ba34347b8960c0450e92622177869a6ae37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabian=20W=C3=BCthrich?= <me@fabwu.ch>
Date: Thu, 6 May 2021 07:52:44 +0200
Subject: [PATCH] cio2-bridge: Use correct dev_properties size
Patchset: cameras
---
drivers/media/pci/intel/ipu3/cio2-bridge.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
index e1e388cc9f45..deaf5804f70d 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.h
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -124,7 +124,7 @@ struct cio2_sensor {
struct cio2_property_names prop_names;
struct property_entry ep_properties[5];
- struct property_entry dev_properties[3];
+ struct property_entry dev_properties[4];
struct property_entry cio2_properties[3];
struct software_node_ref_args local_ref[1];
struct software_node_ref_args remote_ref[1];
--
2.33.0
From b8b2e8e1b91d360c294c0b9260b249eb90be898e Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 20 May 2021 23:31:04 +0100
Subject: [PATCH] media: i2c: Fix vertical flip in ov5693
The pinkness experienced by users with rotated sensors in their laptops
was due to an incorrect setting for the vertical flip function; the
datasheet for the sensor gives the settings as bits 1&2 in one place and
bits 1&6 in another.
Switch to flipping bit 6 instead of bit 2 for 0x3820 in the vertical
flip function to fix the pink hue.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 276f625d4d23..1653fb49f6e0 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -133,7 +133,7 @@
#define OV5693_SUB_INC_Y_REG 0x3815
#define OV5693_FORMAT1_REG 0x3820
-#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(2)
+#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(6)
#define OV5693_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1)
#define OV5693_FORMAT1_VBIN_EN BIT(0)
#define OV5693_FORMAT2_REG 0x3821
--
2.33.0
From 8898454c0454f212a0aecef0b880cf51c5b0c9b1 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 9 Jul 2021 16:39:18 +0100
Subject: [PATCH] media: i2c: Add ACPI support to ov8865
The ov8865 sensor is sometimes found on x86 platforms enumerated via ACPI.
Add an ACPI match table to the driver so that it's probed on those
platforms.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 9ecf180635ee..a28adf45b1b1 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -5,6 +5,7 @@
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -2948,6 +2949,12 @@ static const struct dev_pm_ops ov8865_pm_ops = {
SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL)
};
+static const struct acpi_device_id ov8865_acpi_match[] = {
+ {"INT347A"},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, ov8865_acpi_match);
+
static const struct of_device_id ov8865_of_match[] = {
{ .compatible = "ovti,ov8865" },
{ }
@@ -2958,6 +2965,7 @@ static struct i2c_driver ov8865_driver = {
.driver = {
.name = "ov8865",
.of_match_table = ov8865_of_match,
+ .acpi_match_table = ov8865_acpi_match,
.pm = &ov8865_pm_ops,
},
.probe_new = ov8865_probe,
--
2.33.0
From 9ca51e2ceb46d583c14f3aa3f048e2affe6bbcf7 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Jul 2021 21:20:17 +0100
Subject: [PATCH] media: i2c: Fix incorrect value in comment
The PLL configuration defined here sets 72MHz (which is correct), not
80MHz. Correct the comment.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index a28adf45b1b1..7d716b0d47c1 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -713,7 +713,7 @@ static const struct ov8865_pll2_config ov8865_pll2_config_native = {
/*
* EXTCLK = 24 MHz
* DAC_CLK = 360 MHz
- * SCLK = 80 MHz
+ * SCLK = 72 MHz
*/
static const struct ov8865_pll2_config ov8865_pll2_config_binning = {
--
2.33.0
From 3b21a856d783be36d9f2ed2759e51c44163cae18 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Jul 2021 22:21:52 +0100
Subject: [PATCH] media: i2c: Check fwnode->secondary for endpoint
The ov8865 driver is one of those that can be connected to a CIO2
device by the cio2-bridge code. This means that the absence of an
endpoint for this device is not necessarily fatal, as one might be
built by the cio2-bridge when it probes. Check fwnode->secondary for
an endpoint, and defer probing if one isn't found rather than fail.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 7d716b0d47c1..5fb290a6fc6a 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -2781,6 +2781,7 @@ static int ov8865_resume(struct device *dev)
static int ov8865_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
struct fwnode_handle *handle;
struct ov8865_sensor *sensor;
struct v4l2_subdev *subdev;
@@ -2797,11 +2798,11 @@ static int ov8865_probe(struct i2c_client *client)
/* Graph Endpoint */
- handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
- if (!handle) {
- dev_err(dev, "unable to find endpoint node\n");
- return -EINVAL;
- }
+ handle = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!handle && !IS_ERR_OR_NULL(fwnode->secondary))
+ handle = fwnode_graph_get_next_endpoint(fwnode->secondary, NULL);
+ if (!handle)
+ return -EPROBE_DEFER;
sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
--
2.33.0
From e43b667cb36e343789bf55168b4c6622642356df Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Jul 2021 22:00:25 +0100
Subject: [PATCH] media: i2c: Support 19.2MHz input clock in ov8865
The ov8865 driver as written expects a 24MHz input clock, but the sensor
is sometimes found on x86 platforms with a 19.2MHz input clock supplied.
Add a set of PLL configurations to the driver to support that rate too.
As ACPI doesn't auto-configure the clock rate, check for a clock-frequency
during probe and set that rate if one is found.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 157 +++++++++++++++++++++++++++----------
1 file changed, 114 insertions(+), 43 deletions(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 5fb290a6fc6a..cae7dc9da49d 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -21,10 +21,6 @@
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-mediabus.h>
-/* Clock rate */
-
-#define OV8865_EXTCLK_RATE 24000000
-
/* Register definitions */
/* System */
@@ -665,6 +661,9 @@ struct ov8865_sensor {
struct regulator *avdd;
struct regulator *dvdd;
struct regulator *dovdd;
+
+ unsigned long extclk_rate;
+ unsigned int extclk_rate_idx;
struct clk *extclk;
struct v4l2_fwnode_endpoint endpoint;
@@ -680,49 +679,83 @@ struct ov8865_sensor {
/* Static definitions */
/*
- * EXTCLK = 24 MHz
* PHY_SCLK = 720 MHz
* MIPI_PCLK = 90 MHz
*/
-static const struct ov8865_pll1_config ov8865_pll1_config_native = {
- .pll_pre_div_half = 1,
- .pll_pre_div = 0,
- .pll_mul = 30,
- .m_div = 1,
- .mipi_div = 3,
- .pclk_div = 1,
- .sys_pre_div = 1,
- .sys_div = 2,
+
+static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {
+ { /* 19.2 MHz input clock */
+ .pll_pre_div_half = 1,
+ .pll_pre_div = 2,
+ .pll_mul = 75,
+ .m_div = 1,
+ .mipi_div = 3,
+ .pclk_div = 1,
+ .sys_pre_div = 1,
+ .sys_div = 2,
+ },
+ { /* 24MHz input clock */
+ .pll_pre_div_half = 1,
+ .pll_pre_div = 0,
+ .pll_mul = 30,
+ .m_div = 1,
+ .mipi_div = 3,
+ .pclk_div = 1,
+ .sys_pre_div = 1,
+ .sys_div = 2,
+ },
};
/*
- * EXTCLK = 24 MHz
* DAC_CLK = 360 MHz
* SCLK = 144 MHz
*/
-static const struct ov8865_pll2_config ov8865_pll2_config_native = {
- .pll_pre_div_half = 1,
- .pll_pre_div = 0,
- .pll_mul = 30,
- .dac_div = 2,
- .sys_pre_div = 5,
- .sys_div = 0,
+static const struct ov8865_pll2_config ov8865_pll2_configs_native[] = {
+ /* 19.2MHz input clock */
+ {
+ .pll_pre_div_half = 1,
+ .pll_pre_div = 5,
+ .pll_mul = 75,
+ .dac_div = 1,
+ .sys_pre_div = 1,
+ .sys_div = 3,
+ },
+ /* 24MHz input clock */
+ {
+ .pll_pre_div_half = 1,
+ .pll_pre_div = 0,
+ .pll_mul = 30,
+ .dac_div = 2,
+ .sys_pre_div = 5,
+ .sys_div = 0,
+ }
};
/*
- * EXTCLK = 24 MHz
* DAC_CLK = 360 MHz
* SCLK = 72 MHz
*/
-static const struct ov8865_pll2_config ov8865_pll2_config_binning = {
+static const struct ov8865_pll2_config ov8865_pll2_configs_binning[] = {
+ /* 19.2MHz input clock */
+ {
+ .pll_pre_div_half = 1,
+ .pll_pre_div = 2,
+ .pll_mul = 75,
+ .dac_div = 2,
+ .sys_pre_div = 10,
+ .sys_div = 0,
+ },
+ /* 24MHz input clock */
+ {
.pll_pre_div_half = 1,
.pll_pre_div = 0,
.pll_mul = 30,
.dac_div = 2,
.sys_pre_div = 10,
.sys_div = 0,
+ }
};
static const struct ov8865_sclk_config ov8865_sclk_config_native = {
@@ -934,8 +967,8 @@ static const struct ov8865_mode ov8865_modes[] = {
.frame_interval = { 1, 30 },
/* PLL */
- .pll1_config = &ov8865_pll1_config_native,
- .pll2_config = &ov8865_pll2_config_native,
+ .pll1_config = ov8865_pll1_configs_native,
+ .pll2_config = ov8865_pll2_configs_native,
.sclk_config = &ov8865_sclk_config_native,
/* Registers */
@@ -990,8 +1023,8 @@ static const struct ov8865_mode ov8865_modes[] = {
.frame_interval = { 1, 30 },
/* PLL */
- .pll1_config = &ov8865_pll1_config_native,
- .pll2_config = &ov8865_pll2_config_native,
+ .pll1_config = ov8865_pll1_configs_native,
+ .pll2_config = ov8865_pll2_configs_native,
.sclk_config = &ov8865_sclk_config_native,
/* Registers */
@@ -1050,8 +1083,8 @@ static const struct ov8865_mode ov8865_modes[] = {
.frame_interval = { 1, 30 },
/* PLL */
- .pll1_config = &ov8865_pll1_config_native,
- .pll2_config = &ov8865_pll2_config_binning,
+ .pll1_config = ov8865_pll1_configs_native,
+ .pll2_config = ov8865_pll2_configs_binning,
.sclk_config = &ov8865_sclk_config_native,
/* Registers */
@@ -1116,8 +1149,8 @@ static const struct ov8865_mode ov8865_modes[] = {
.frame_interval = { 1, 90 },
/* PLL */
- .pll1_config = &ov8865_pll1_config_native,
- .pll2_config = &ov8865_pll2_config_binning,
+ .pll1_config = ov8865_pll1_configs_native,
+ .pll2_config = ov8865_pll2_configs_binning,
.sclk_config = &ov8865_sclk_config_native,
/* Registers */
@@ -1266,6 +1299,13 @@ static const struct ov8865_register_value ov8865_init_sequence[] = {
{ 0x4503, 0x10 },
};
+/* Clock rate */
+
+static const unsigned long supported_extclk_rates[] = {
+ 19200000,
+ 24000000,
+};
+
static const s64 ov8865_link_freq_menu[] = {
360000000,
};
@@ -1513,12 +1553,11 @@ static int ov8865_isp_configure(struct ov8865_sensor *sensor)
static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,
const struct ov8865_mode *mode)
{
- const struct ov8865_pll1_config *config = mode->pll1_config;
- unsigned long extclk_rate;
+ const struct ov8865_pll1_config *config;
unsigned long pll1_rate;
- extclk_rate = clk_get_rate(sensor->extclk);
- pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half;
+ config = &mode->pll1_config[sensor->extclk_rate_idx];
+ pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half;
switch (config->pll_pre_div) {
case 0:
@@ -1552,10 +1591,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
const struct ov8865_mode *mode,
u32 mbus_code)
{
- const struct ov8865_pll1_config *config = mode->pll1_config;
+ const struct ov8865_pll1_config *config;
u8 value;
int ret;
+ config = &mode->pll1_config[sensor->extclk_rate_idx];
+
switch (mbus_code) {
case MEDIA_BUS_FMT_SBGGR10_1X10:
value = OV8865_MIPI_BIT_SEL(10);
@@ -1622,9 +1663,11 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,
const struct ov8865_mode *mode)
{
- const struct ov8865_pll2_config *config = mode->pll2_config;
+ const struct ov8865_pll2_config *config;
int ret;
+ config = &mode->pll2_config[sensor->extclk_rate_idx];
+
ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,
OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |
OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));
@@ -2053,9 +2096,11 @@ static int ov8865_mode_configure(struct ov8865_sensor *sensor,
static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,
const struct ov8865_mode *mode)
{
- const struct ov8865_pll1_config *config = mode->pll1_config;
+ const struct ov8865_pll1_config *config;
unsigned long pll1_rate;
+ config = &mode->pll1_config[sensor->extclk_rate_idx];
+
pll1_rate = ov8865_mode_pll1_rate(sensor, mode);
return pll1_rate / config->m_div / 2;
@@ -2786,7 +2831,8 @@ static int ov8865_probe(struct i2c_client *client)
struct ov8865_sensor *sensor;
struct v4l2_subdev *subdev;
struct media_pad *pad;
- unsigned long rate;
+ unsigned int rate;
+ unsigned int i;
int ret;
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -2863,13 +2909,38 @@ static int ov8865_probe(struct i2c_client *client)
goto error_endpoint;
}
- rate = clk_get_rate(sensor->extclk);
- if (rate != OV8865_EXTCLK_RATE) {
- dev_err(dev, "clock rate %lu Hz is unsupported\n", rate);
+ /*
+ * We could have either a 24MHz or 19.2MHz clock rate. Check for a
+ * clock-frequency property and if found, set that rate. This should
+ * cover ACPI case. If the system uses devicetree then the configured
+ * rate should already be set, so we'll have to check it.
+ */
+
+ ret = fwnode_property_read_u32(fwnode, "clock-frequency", &rate);
+ if (!ret) {
+ ret = clk_set_rate(sensor->extclk, rate);
+ if (ret) {
+ dev_err(dev, "failed to set clock rate\n");
+ return ret;
+ }
+ }
+
+ sensor->extclk_rate = clk_get_rate(sensor->extclk);
+
+ for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) {
+ if (sensor->extclk_rate == supported_extclk_rates[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(supported_extclk_rates)) {
+ dev_err(dev, "clock rate %lu Hz is unsupported\n",
+ sensor->extclk_rate);
ret = -EINVAL;
goto error_endpoint;
}
+ sensor->extclk_rate_idx = i;
+
/* Subdev, entity and pad */
subdev = &sensor->subdev;
--
2.33.0
From 13f5cd8218ed183accc1ea89780b42355ef1b522 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Jul 2021 22:19:10 +0100
Subject: [PATCH] media: i2c: Add .get_selection() support to ov8865
The ov8865 drivers media pad ops currently does not include
.get_selection() - add support for that callback.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 61 ++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index cae7dc9da49d..3ce0af7e0054 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -450,6 +450,15 @@
#define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2
#define OV8865_PRE_CTRL0_PATTERN_BLACK 3
+/* Pixel Array */
+
+#define OV8865_NATIVE_WIDTH 3296
+#define OV8865_NATIVE_HEIGHT 2528
+#define OV8865_ACTIVE_START_TOP 32
+#define OV8865_ACTIVE_START_LEFT 80
+#define OV8865_ACTIVE_WIDTH 3264
+#define OV8865_ACTIVE_HEIGHT 2448
+
/* Macros */
#define ov8865_subdev_sensor(s) \
@@ -2745,12 +2754,64 @@ static int ov8865_enum_frame_interval(struct v4l2_subdev *subdev,
return 0;
}
+static void
+__ov8865_get_pad_crop(struct ov8865_sensor *sensor,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ enum v4l2_subdev_format_whence which, struct v4l2_rect *r)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ *r = *v4l2_subdev_get_try_crop(&sensor->subdev, cfg, pad);
+ break;
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ r->height = sensor->state.mode->output_size_y;
+ r->width = sensor->state.mode->output_size_x;
+ r->top = (OV8865_NATIVE_HEIGHT - sensor->state.mode->output_size_y) / 2;
+ r->left = (OV8865_NATIVE_WIDTH - sensor->state.mode->output_size_x) / 2;
+ break;
+ }
+}
+
+static int ov8865_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ mutex_lock(&sensor->mutex);
+ __ov8865_get_pad_crop(sensor, cfg, sel->pad,
+ sel->which, &sel->r);
+ mutex_unlock(&sensor->mutex);
+ break;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = OV8865_NATIVE_WIDTH;
+ sel->r.height = OV8865_NATIVE_HEIGHT;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.top = OV8865_ACTIVE_START_TOP;
+ sel->r.left = OV8865_ACTIVE_START_LEFT;
+ sel->r.width = OV8865_ACTIVE_WIDTH;
+ sel->r.height = OV8865_ACTIVE_HEIGHT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = {
.enum_mbus_code = ov8865_enum_mbus_code,
.get_fmt = ov8865_get_fmt,
.set_fmt = ov8865_set_fmt,
.enum_frame_size = ov8865_enum_frame_size,
.enum_frame_interval = ov8865_enum_frame_interval,
+ .get_selection = ov8865_get_selection,
};
static const struct v4l2_subdev_ops ov8865_subdev_ops = {
--
2.33.0
From b08e46416ae81314e89b7b3e74c0c69de1f41f4e Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Jul 2021 22:34:43 +0100
Subject: [PATCH] media: i2c: Switch control to V4L2_CID_ANALOGUE_GAIN
The V4L2_CID_GAIN control for this driver configures registers that
the datasheet specifies as analogue gain. Switch the control's ID
to V4L2_CID_ANALOGUE_GAIN.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 3ce0af7e0054..c0c6b1d7e1ed 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -2137,7 +2137,7 @@ static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure)
/* Gain */
-static int ov8865_gain_configure(struct ov8865_sensor *sensor, u32 gain)
+static int ov8865_analog_gain_configure(struct ov8865_sensor *sensor, u32 gain)
{
int ret;
@@ -2447,8 +2447,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
if (ret)
return ret;
break;
- case V4L2_CID_GAIN:
- ret = ov8865_gain_configure(sensor, ctrl->val);
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov8865_analog_gain_configure(sensor, ctrl->val);
if (ret)
return ret;
break;
@@ -2493,7 +2493,7 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
/* Gain */
- v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 128, 8191, 128, 128);
+ v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 8191, 128, 128);
/* White Balance */
--
2.33.0
From aa022a89b38f9807db2f243e21b2c4b33a0f51cc Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 12 Jul 2021 22:54:56 +0100
Subject: [PATCH] media: i2c: Add vblank control to ov8865
Add a V4L2_CID_VBLANK control to the ov8865 driver.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index c0c6b1d7e1ed..5f67d85e33bc 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -183,6 +183,8 @@
#define OV8865_VTS_H(v) (((v) & GENMASK(11, 8)) >> 8)
#define OV8865_VTS_L_REG 0x380f
#define OV8865_VTS_L(v) ((v) & GENMASK(7, 0))
+#define OV8865_TIMING_MAX_VTS 0xffff
+#define OV8865_TIMING_MIN_VTS 0x04
#define OV8865_OFFSET_X_H_REG 0x3810
#define OV8865_OFFSET_X_H(v) (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_OFFSET_X_L_REG 0x3811
@@ -658,6 +660,7 @@ struct ov8865_state {
struct ov8865_ctrls {
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
struct v4l2_ctrl_handler handler;
};
@@ -2212,6 +2215,20 @@ static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor,
ov8865_test_pattern_bits[index]);
}
+/* Blanking */
+
+static int ov8865_vts_configure(struct ov8865_sensor *sensor, u32 vblank)
+{
+ u16 vts = sensor->state.mode->output_size_y + vblank;
+ int ret;
+
+ ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(vts));
+ if (ret)
+ return ret;
+
+ return ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(vts));
+}
+
/* State */
static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor,
@@ -2463,6 +2480,8 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
index = (unsigned int)ctrl->val;
return ov8865_test_pattern_configure(sensor, index);
+ case V4L2_CID_VBLANK:
+ return ov8865_vts_configure(sensor, ctrl->val);
default:
return -EINVAL;
}
@@ -2479,6 +2498,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
struct ov8865_ctrls *ctrls = &sensor->ctrls;
struct v4l2_ctrl_handler *handler = &ctrls->handler;
const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops;
+ const struct ov8865_mode *mode = sensor->state.mode;
+ unsigned int vblank_max, vblank_def;
int ret;
v4l2_ctrl_handler_init(handler, 32);
@@ -2514,6 +2535,13 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
ARRAY_SIZE(ov8865_test_pattern_menu) - 1,
0, 0, ov8865_test_pattern_menu);
+ /* Blanking */
+ vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y;
+ vblank_def = mode->vts - mode->output_size_y;
+ ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK,
+ OV8865_TIMING_MIN_VTS, vblank_max, 1,
+ vblank_def);
+
/* MIPI CSI-2 */
ctrls->link_freq =
@@ -2696,6 +2724,10 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev,
sensor->state.mbus_code != mbus_code)
ret = ov8865_state_configure(sensor, mode, mbus_code);
+ __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV8865_TIMING_MIN_VTS,
+ OV8865_TIMING_MAX_VTS - mode->output_size_y,
+ 1, mode->vts - mode->output_size_y);
+
complete:
mutex_unlock(&sensor->mutex);
@@ -3023,6 +3055,8 @@ static int ov8865_probe(struct i2c_client *client)
/* Sensor */
+ sensor->state.mode = &ov8865_modes[0];
+
ret = ov8865_ctrls_init(sensor);
if (ret)
goto error_mutex;
--
2.33.0
From 11edb4401ec485ac51a517be5f1e3c1b11957c71 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Tue, 13 Jul 2021 23:40:33 +0100
Subject: [PATCH] media: i2c: Add hblank control to ov8865
Add a V4L2_CID_HBLANK control to the ov8865 driver. This is read only
with timing control intended to be done via vblanking alone.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 5f67d85e33bc..66754ff62a22 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -660,6 +660,7 @@ struct ov8865_state {
struct ov8865_ctrls {
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_ctrl_handler handler;
@@ -2500,6 +2501,7 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops;
const struct ov8865_mode *mode = sensor->state.mode;
unsigned int vblank_max, vblank_def;
+ unsigned int hblank;
int ret;
v4l2_ctrl_handler_init(handler, 32);
@@ -2536,6 +2538,13 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
0, 0, ov8865_test_pattern_menu);
/* Blanking */
+ hblank = mode->hts < mode->output_size_x ? 0 : mode->hts - mode->output_size_x;
+ ctrls->hblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HBLANK, hblank,
+ hblank, 1, hblank);
+
+ if (ctrls->hblank)
+ ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y;
vblank_def = mode->vts - mode->output_size_y;
ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK,
@@ -2684,6 +2693,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev,
struct v4l2_mbus_framefmt *mbus_format = &format->format;
const struct ov8865_mode *mode;
u32 mbus_code = 0;
+ unsigned int hblank;
unsigned int index;
int ret = 0;
@@ -2728,6 +2738,10 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev,
OV8865_TIMING_MAX_VTS - mode->output_size_y,
1, mode->vts - mode->output_size_y);
+ hblank = mode->hts < mode->output_size_x ? 0 : mode->hts - mode->output_size_x;
+ __v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1,
+ hblank);
+
complete:
mutex_unlock(&sensor->mutex);
--
2.33.0
From d27a9793580dcf69c63178e50c1935cd2636ec82 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Tue, 13 Jul 2021 23:43:17 +0100
Subject: [PATCH] media: i2c: cap exposure at height + vblank in ov8865
Exposure limits depend on the total height; when vblank is altered (and
thus the total height is altered), change the exposure limits to reflect
the new cap.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 66754ff62a22..93e741952050 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -662,6 +662,7 @@ struct ov8865_ctrls {
struct v4l2_ctrl *pixel_rate;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
struct v4l2_ctrl_handler handler;
};
@@ -2455,6 +2456,18 @@ static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
unsigned int index;
int ret;
+ /* If VBLANK is altered we need to update exposure to compensate */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ int exposure_max;
+
+ exposure_max = sensor->state.mode->output_size_y + ctrl->val;
+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure,
+ sensor->ctrls.exposure->minimum,
+ exposure_max,
+ sensor->ctrls.exposure->step,
+ min(sensor->ctrls.exposure->val, exposure_max));
+ }
+
/* Wait for the sensor to be on before setting controls. */
if (pm_runtime_suspended(sensor->dev))
return 0;
@@ -2511,8 +2524,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
/* Exposure */
- v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16,
- 512);
+ ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16,
+ 1048575, 16, 512);
/* Gain */
@@ -2695,6 +2708,7 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev,
u32 mbus_code = 0;
unsigned int hblank;
unsigned int index;
+ int exposure_max;
int ret = 0;
mutex_lock(&sensor->mutex);
@@ -2742,6 +2756,12 @@ static int ov8865_set_fmt(struct v4l2_subdev *subdev,
__v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1,
hblank);
+ exposure_max = mode->vts;
+ __v4l2_ctrl_modify_range(sensor->ctrls.exposure,
+ sensor->ctrls.exposure->minimum, exposure_max,
+ sensor->ctrls.exposure->step,
+ min(sensor->ctrls.exposure->val, exposure_max));
+
complete:
mutex_unlock(&sensor->mutex);
--
2.33.0
From 15e734a203c861eac5794ccd6cfd1ccc31e4d9b9 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 14 Jul 2021 18:05:44 +0100
Subject: [PATCH] media: i2c: Remove unused macros from ov8865
There are a number of macros defined in this driver that aren't actually
used within it. There's a lot of macros defined in total, so removing the
unused ones helps make it a bit less busy.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 137 +------------------------------------
1 file changed, 1 insertion(+), 136 deletions(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 93e741952050..fd4de2ea1fa9 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -46,8 +46,6 @@
#define OV8865_PLL_CTRL6_REG 0x306
#define OV8865_PLL_CTRL6_SYS_DIV(v) (((v) - 1) & BIT(0))
-#define OV8865_PLL_CTRL8_REG 0x308
-#define OV8865_PLL_CTRL9_REG 0x309
#define OV8865_PLL_CTRLA_REG 0x30a
#define OV8865_PLL_CTRLA_PRE_DIV_HALF(v) (((v) - 1) & BIT(0))
#define OV8865_PLL_CTRLB_REG 0x30b
@@ -60,41 +58,21 @@
#define OV8865_PLL_CTRLE_SYS_DIV(v) ((v) & GENMASK(2, 0))
#define OV8865_PLL_CTRLF_REG 0x30f
#define OV8865_PLL_CTRLF_SYS_PRE_DIV(v) (((v) - 1) & GENMASK(3, 0))
-#define OV8865_PLL_CTRL10_REG 0x310
-#define OV8865_PLL_CTRL11_REG 0x311
#define OV8865_PLL_CTRL12_REG 0x312
#define OV8865_PLL_CTRL12_PRE_DIV_HALF(v) ((((v) - 1) << 4) & BIT(4))
#define OV8865_PLL_CTRL12_DAC_DIV(v) (((v) - 1) & GENMASK(3, 0))
-#define OV8865_PLL_CTRL1B_REG 0x31b
-#define OV8865_PLL_CTRL1C_REG 0x31c
-
#define OV8865_PLL_CTRL1E_REG 0x31e
#define OV8865_PLL_CTRL1E_PLL1_NO_LAT BIT(3)
-#define OV8865_PAD_OEN0_REG 0x3000
-
-#define OV8865_PAD_OEN2_REG 0x3002
-
-#define OV8865_CLK_RST5_REG 0x3005
-
#define OV8865_CHIP_ID_HH_REG 0x300a
#define OV8865_CHIP_ID_HH_VALUE 0x00
#define OV8865_CHIP_ID_H_REG 0x300b
#define OV8865_CHIP_ID_H_VALUE 0x88
#define OV8865_CHIP_ID_L_REG 0x300c
#define OV8865_CHIP_ID_L_VALUE 0x65
-#define OV8865_PAD_OUT2_REG 0x300d
-
-#define OV8865_PAD_SEL2_REG 0x3010
-#define OV8865_PAD_PK_REG 0x3011
-#define OV8865_PAD_PK_DRIVE_STRENGTH_1X (0 << 5)
-#define OV8865_PAD_PK_DRIVE_STRENGTH_2X (1 << 5)
-#define OV8865_PAD_PK_DRIVE_STRENGTH_3X (2 << 5)
-#define OV8865_PAD_PK_DRIVE_STRENGTH_4X (3 << 5)
#define OV8865_PUMP_CLK_DIV_REG 0x3015
-#define OV8865_PUMP_CLK_DIV_PUMP_N(v) (((v) << 4) & GENMASK(6, 4))
#define OV8865_PUMP_CLK_DIV_PUMP_P(v) ((v) & GENMASK(2, 0))
#define OV8865_MIPI_SC_CTRL0_REG 0x3018
@@ -102,21 +80,12 @@
GENMASK(7, 5))
#define OV8865_MIPI_SC_CTRL0_MIPI_EN BIT(4)
#define OV8865_MIPI_SC_CTRL0_UNKNOWN BIT(1)
-#define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI BIT(0)
-#define OV8865_MIPI_SC_CTRL1_REG 0x3019
-#define OV8865_CLK_RST0_REG 0x301a
-#define OV8865_CLK_RST1_REG 0x301b
-#define OV8865_CLK_RST2_REG 0x301c
-#define OV8865_CLK_RST3_REG 0x301d
-#define OV8865_CLK_RST4_REG 0x301e
#define OV8865_PCLK_SEL_REG 0x3020
#define OV8865_PCLK_SEL_PCLK_DIV_MASK BIT(3)
#define OV8865_PCLK_SEL_PCLK_DIV(v) ((((v) - 1) << 3) & BIT(3))
-#define OV8865_MISC_CTRL_REG 0x3021
#define OV8865_MIPI_SC_CTRL2_REG 0x3022
-#define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI BIT(1)
#define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC BIT(0)
#define OV8865_MIPI_BIT_SEL_REG 0x3031
@@ -125,7 +94,6 @@
#define OV8865_CLK_SEL0_PLL1_SYS_SEL(v) (((v) << 7) & BIT(7))
#define OV8865_CLK_SEL1_REG 0x3033
#define OV8865_CLK_SEL1_MIPI_EOF BIT(5)
-#define OV8865_CLK_SEL1_UNKNOWN BIT(2)
#define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK BIT(1)
#define OV8865_CLK_SEL1_PLL_SCLK_SEL(v) (((v) << 1) & BIT(1))
@@ -142,7 +110,6 @@
#define OV8865_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8)
#define OV8865_EXPOSURE_CTRL_L_REG 0x3502
#define OV8865_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0))
-#define OV8865_EXPOSURE_GAIN_MANUAL_REG 0x3503
#define OV8865_GAIN_CTRL_H_REG 0x3508
#define OV8865_GAIN_CTRL_H(v) (((v) & GENMASK(12, 8)) >> 8)
@@ -197,18 +164,6 @@
#define OV8865_INC_X_ODD(v) ((v) & GENMASK(4, 0))
#define OV8865_INC_X_EVEN_REG 0x3815
#define OV8865_INC_X_EVEN(v) ((v) & GENMASK(4, 0))
-#define OV8865_VSYNC_START_H_REG 0x3816
-#define OV8865_VSYNC_START_H(v) (((v) & GENMASK(15, 8)) >> 8)
-#define OV8865_VSYNC_START_L_REG 0x3817
-#define OV8865_VSYNC_START_L(v) ((v) & GENMASK(7, 0))
-#define OV8865_VSYNC_END_H_REG 0x3818
-#define OV8865_VSYNC_END_H(v) (((v) & GENMASK(15, 8)) >> 8)
-#define OV8865_VSYNC_END_L_REG 0x3819
-#define OV8865_VSYNC_END_L(v) ((v) & GENMASK(7, 0))
-#define OV8865_HSYNC_FIRST_H_REG 0x381a
-#define OV8865_HSYNC_FIRST_H(v) (((v) & GENMASK(15, 8)) >> 8)
-#define OV8865_HSYNC_FIRST_L_REG 0x381b
-#define OV8865_HSYNC_FIRST_L(v) ((v) & GENMASK(7, 0))
#define OV8865_FORMAT1_REG 0x3820
#define OV8865_FORMAT1_FLIP_VERT_ISP_EN BIT(2)
@@ -240,10 +195,6 @@
#define OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG BIT(2)
#define OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG BIT(1)
#define OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG BIT(0)
-#define OV8865_AUTO_SIZE_X_OFFSET_H_REG 0x3842
-#define OV8865_AUTO_SIZE_X_OFFSET_L_REG 0x3843
-#define OV8865_AUTO_SIZE_Y_OFFSET_H_REG 0x3844
-#define OV8865_AUTO_SIZE_Y_OFFSET_L_REG 0x3845
#define OV8865_AUTO_SIZE_BOUNDARIES_REG 0x3846
#define OV8865_AUTO_SIZE_BOUNDARIES_Y(v) (((v) << 4) & GENMASK(7, 4))
#define OV8865_AUTO_SIZE_BOUNDARIES_X(v) ((v) & GENMASK(3, 0))
@@ -259,30 +210,10 @@
#define OV8865_BLC_CTRL0_TRIG_FORMAT_EN BIT(6)
#define OV8865_BLC_CTRL0_TRIG_GAIN_EN BIT(5)
#define OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN BIT(4)
-#define OV8865_BLC_CTRL0_TRIG_MANUAL_EN BIT(3)
-#define OV8865_BLC_CTRL0_FREEZE_EN BIT(2)
-#define OV8865_BLC_CTRL0_ALWAYS_EN BIT(1)
#define OV8865_BLC_CTRL0_FILTER_EN BIT(0)
#define OV8865_BLC_CTRL1_REG 0x4001
-#define OV8865_BLC_CTRL1_DITHER_EN BIT(7)
-#define OV8865_BLC_CTRL1_ZERO_LINE_DIFF_EN BIT(6)
-#define OV8865_BLC_CTRL1_COL_SHIFT_256 (0 << 4)
#define OV8865_BLC_CTRL1_COL_SHIFT_128 (1 << 4)
-#define OV8865_BLC_CTRL1_COL_SHIFT_64 (2 << 4)
-#define OV8865_BLC_CTRL1_COL_SHIFT_32 (3 << 4)
#define OV8865_BLC_CTRL1_OFFSET_LIMIT_EN BIT(2)
-#define OV8865_BLC_CTRL1_COLUMN_CANCEL_EN BIT(1)
-#define OV8865_BLC_CTRL2_REG 0x4002
-#define OV8865_BLC_CTRL3_REG 0x4003
-#define OV8865_BLC_CTRL4_REG 0x4004
-#define OV8865_BLC_CTRL5_REG 0x4005
-#define OV8865_BLC_CTRL6_REG 0x4006
-#define OV8865_BLC_CTRL7_REG 0x4007
-#define OV8865_BLC_CTRL8_REG 0x4008
-#define OV8865_BLC_CTRL9_REG 0x4009
-#define OV8865_BLC_CTRLA_REG 0x400a
-#define OV8865_BLC_CTRLB_REG 0x400b
-#define OV8865_BLC_CTRLC_REG 0x400c
#define OV8865_BLC_CTRLD_REG 0x400d
#define OV8865_BLC_CTRLD_OFFSET_TRIGGER(v) ((v) & GENMASK(7, 0))
@@ -337,66 +268,8 @@
/* MIPI */
-#define OV8865_MIPI_CTRL0_REG 0x4800
-#define OV8865_MIPI_CTRL1_REG 0x4801
-#define OV8865_MIPI_CTRL2_REG 0x4802
-#define OV8865_MIPI_CTRL3_REG 0x4803
-#define OV8865_MIPI_CTRL4_REG 0x4804
-#define OV8865_MIPI_CTRL5_REG 0x4805
-#define OV8865_MIPI_CTRL6_REG 0x4806
-#define OV8865_MIPI_CTRL7_REG 0x4807
-#define OV8865_MIPI_CTRL8_REG 0x4808
-
-#define OV8865_MIPI_FCNT_MAX_H_REG 0x4810
-#define OV8865_MIPI_FCNT_MAX_L_REG 0x4811
-
-#define OV8865_MIPI_CTRL13_REG 0x4813
-#define OV8865_MIPI_CTRL14_REG 0x4814
-#define OV8865_MIPI_CTRL15_REG 0x4815
-#define OV8865_MIPI_EMBEDDED_DT_REG 0x4816
-
-#define OV8865_MIPI_HS_ZERO_MIN_H_REG 0x4818
-#define OV8865_MIPI_HS_ZERO_MIN_L_REG 0x4819
-#define OV8865_MIPI_HS_TRAIL_MIN_H_REG 0x481a
-#define OV8865_MIPI_HS_TRAIL_MIN_L_REG 0x481b
-#define OV8865_MIPI_CLK_ZERO_MIN_H_REG 0x481c
-#define OV8865_MIPI_CLK_ZERO_MIN_L_REG 0x481d
-#define OV8865_MIPI_CLK_PREPARE_MAX_REG 0x481e
-#define OV8865_MIPI_CLK_PREPARE_MIN_REG 0x481f
-#define OV8865_MIPI_CLK_POST_MIN_H_REG 0x4820
-#define OV8865_MIPI_CLK_POST_MIN_L_REG 0x4821
-#define OV8865_MIPI_CLK_TRAIL_MIN_H_REG 0x4822
-#define OV8865_MIPI_CLK_TRAIL_MIN_L_REG 0x4823
-#define OV8865_MIPI_LPX_P_MIN_H_REG 0x4824
-#define OV8865_MIPI_LPX_P_MIN_L_REG 0x4825
-#define OV8865_MIPI_HS_PREPARE_MIN_REG 0x4826
-#define OV8865_MIPI_HS_PREPARE_MAX_REG 0x4827
-#define OV8865_MIPI_HS_EXIT_MIN_H_REG 0x4828
-#define OV8865_MIPI_HS_EXIT_MIN_L_REG 0x4829
-#define OV8865_MIPI_UI_HS_ZERO_MIN_REG 0x482a
-#define OV8865_MIPI_UI_HS_TRAIL_MIN_REG 0x482b
-#define OV8865_MIPI_UI_CLK_ZERO_MIN_REG 0x482c
-#define OV8865_MIPI_UI_CLK_PREPARE_REG 0x482d
-#define OV8865_MIPI_UI_CLK_POST_MIN_REG 0x482e
-#define OV8865_MIPI_UI_CLK_TRAIL_MIN_REG 0x482f
-#define OV8865_MIPI_UI_LPX_P_MIN_REG 0x4830
-#define OV8865_MIPI_UI_HS_PREPARE_REG 0x4831
-#define OV8865_MIPI_UI_HS_EXIT_MIN_REG 0x4832
-#define OV8865_MIPI_PKT_START_SIZE_REG 0x4833
-
#define OV8865_MIPI_PCLK_PERIOD_REG 0x4837
-#define OV8865_MIPI_LP_GPIO0_REG 0x4838
-#define OV8865_MIPI_LP_GPIO1_REG 0x4839
-
-#define OV8865_MIPI_CTRL3C_REG 0x483c
-#define OV8865_MIPI_LP_GPIO4_REG 0x483d
-
-#define OV8865_MIPI_CTRL4A_REG 0x484a
-#define OV8865_MIPI_CTRL4B_REG 0x484b
-#define OV8865_MIPI_CTRL4C_REG 0x484c
-#define OV8865_MIPI_LANE_TEST_PATTERN_REG 0x484d
-#define OV8865_MIPI_FRAME_END_DELAY_REG 0x484e
-#define OV8865_MIPI_CLOCK_TEST_PATTERN_REG 0x484f
+
#define OV8865_MIPI_LANE_SEL01_REG 0x4850
#define OV8865_MIPI_LANE_SEL01_LANE0(v) (((v) << 0) & GENMASK(2, 0))
#define OV8865_MIPI_LANE_SEL01_LANE1(v) (((v) << 4) & GENMASK(6, 4))
@@ -407,7 +280,6 @@
/* ISP */
#define OV8865_ISP_CTRL0_REG 0x5000
-#define OV8865_ISP_CTRL0_LENC_EN BIT(7)
#define OV8865_ISP_CTRL0_WHITE_BALANCE_EN BIT(4)
#define OV8865_ISP_CTRL0_DPC_BLACK_EN BIT(2)
#define OV8865_ISP_CTRL0_DPC_WHITE_EN BIT(1)
@@ -416,17 +288,11 @@
#define OV8865_ISP_CTRL2_REG 0x5002
#define OV8865_ISP_CTRL2_DEBUG BIT(3)
#define OV8865_ISP_CTRL2_VARIOPIXEL_EN BIT(2)
-#define OV8865_ISP_CTRL2_VSYNC_LATCH_EN BIT(0)
-#define OV8865_ISP_CTRL3_REG 0x5003
#define OV8865_ISP_GAIN_RED_H_REG 0x5018
#define OV8865_ISP_GAIN_RED_H(v) (((v) & GENMASK(13, 6)) >> 6)
#define OV8865_ISP_GAIN_RED_L_REG 0x5019
#define OV8865_ISP_GAIN_RED_L(v) ((v) & GENMASK(5, 0))
-#define OV8865_ISP_GAIN_GREEN_H_REG 0x501a
-#define OV8865_ISP_GAIN_GREEN_H(v) (((v) & GENMASK(13, 6)) >> 6)
-#define OV8865_ISP_GAIN_GREEN_L_REG 0x501b
-#define OV8865_ISP_GAIN_GREEN_L(v) ((v) & GENMASK(5, 0))
#define OV8865_ISP_GAIN_BLUE_H_REG 0x501c
#define OV8865_ISP_GAIN_BLUE_H(v) (((v) & GENMASK(13, 6)) >> 6)
#define OV8865_ISP_GAIN_BLUE_L_REG 0x501d
@@ -434,7 +300,6 @@
/* VarioPixel */
-#define OV8865_VAP_CTRL0_REG 0x5900
#define OV8865_VAP_CTRL1_REG 0x5901
#define OV8865_VAP_CTRL1_HSUB_COEF(v) ((((v) - 1) << 2) & \
GENMASK(3, 2))
--
2.33.0
From 0c71885c10af33ed271073d670d03c3ea41fbb65 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 16 Jul 2021 00:00:54 +0100
Subject: [PATCH] media: i2c: Switch exposure control unit to lines
The ov8865 driver currently has the unit of the V4L2_CID_EXPOSURE control
as 1/16th of a line. This is what the sensor expects, but isn't very
intuitive. Switch the control to be in units of a line and simply do the
16x multiplication before passing the value to the sensor.
The datasheet for this sensor gives minimum exposure as 2 lines, so take
the opportunity to correct the lower bounds of the control.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index fd4de2ea1fa9..1905028742d5 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -1991,6 +1991,9 @@ static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure)
{
int ret;
+ /* The sensor stores exposure in units of 1/16th of a line */
+ exposure *= 16;
+
ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG,
OV8865_EXPOSURE_CTRL_HH(exposure));
if (ret)
@@ -2389,8 +2392,8 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
/* Exposure */
- ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16,
- 1048575, 16, 512);
+ ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 2,
+ 65535, 1, 32);
/* Gain */
--
2.33.0
From 14f39d347de9bfd1ff52dfef55ef10a364938a96 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 16 Jul 2021 22:56:15 +0100
Subject: [PATCH] media: i2c: Add controls from fwnode to ov8865
Add V4L2_CID_ORIENTATION and V4L2_CID_ROTATION controls to the ov8865
driver by attempting to parse them from firmware.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov8865.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 1905028742d5..e88825ea76aa 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -2381,6 +2381,7 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
struct v4l2_ctrl_handler *handler = &ctrls->handler;
const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops;
const struct ov8865_mode *mode = sensor->state.mode;
+ struct v4l2_fwnode_device_properties props;
unsigned int vblank_max, vblank_def;
unsigned int hblank;
int ret;
@@ -2443,6 +2444,15 @@ static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1,
INT_MAX, 1, 1);
+ /* set properties from fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
+ if (ret)
+ goto error_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(handler, ops, &props);
+ if (ret)
+ goto error_ctrls;
+
if (handler->error) {
ret = handler->error;
goto error_ctrls;
--
2.33.0
From 3bd30feb956376b0ffff78f2e7b2b7e9bd945608 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 14 Jul 2021 00:05:04 +0100
Subject: [PATCH] media: ipu3-cio2: Add INT347A to cio2-bridge
ACPI _HID INT347A represents the OV8865 sensor, the driver for which can
support the platforms that the cio2-bridge serves. Add it to the array
of supported sensors so the bridge will connect the sensor to the CIO2
device.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/cio2-bridge.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
index 1052885caa25..856dc7439a59 100644
--- a/drivers/media/pci/intel/ipu3/cio2-bridge.c
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -24,6 +24,8 @@ static const struct cio2_sensor_config cio2_supported_sensors[] = {
CIO2_SENSOR_CONFIG("INT33BE", 0),
/* Omnivision OV2680 */
CIO2_SENSOR_CONFIG("OVTI2680", 0),
+ /* Omnivision OV8865 */
+ CIO2_SENSOR_CONFIG("INT347A", 1, 360000000),
};
static const struct cio2_property_names prop_names = {
--
2.33.0