linux-surface/patches/5.11/0009-cameras.patch
Maximilian Luz 99543a73db
Update v5.11 patches
Changes:
 - SAM:
   - Add support for the 13" Intel version of the Surface Laptop 4
   - Small code cleanup

Links:
 - kernel: fbe1313c9b
 - SAM: e24239cf87
2021-05-24 17:28:12 +02:00

11128 lines
326 KiB
Diff

From 0f79e1d515c2d93ba8d1dac9d2e4dcc88995ffd7 Mon Sep 17 00:00:00 2001
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Date: Wed, 30 Dec 2020 22:44:05 +0200
Subject: [PATCH] media: ipu3-cio2: Add headers that ipu3-cio2.h is direct user
of
Add headers that ipu3-cio2.h is direct user of.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Daniel Scally <djrscally@gmail.com>
Tested-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/ipu3-cio2.h | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index ccf0b85ae36f..62187ab5ae43 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -4,8 +4,26 @@
#ifndef __IPU3_CIO2_H
#define __IPU3_CIO2_H
+#include <linux/bits.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
#include <linux/types.h>
+#include <asm/page.h>
+
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+struct cio2_fbpt_entry; /* defined here, after the first usage */
+struct pci_dev;
+
#define CIO2_NAME "ipu3-cio2"
#define CIO2_DEVICE_NAME "Intel IPU3 CIO2"
#define CIO2_ENTITY_NAME "ipu3-csi2"
--
2.31.1
From a341370af7deeb47fee9ece241790c14a7e36682 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 24 Oct 2020 22:42:28 +0100
Subject: [PATCH] device property: Return true in fwnode_device_is_available
for NULL ops
Some types of fwnode_handle do not implement the device_is_available()
check, such as those created by software_nodes. There isn't really a
meaningful way to check for the availability of a device that doesn't
actually exist, so if the check isn't implemented just assume that the
"device" is present.
Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/property.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 35b95c6ac0c6..0bf5260f14c6 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -837,9 +837,15 @@ EXPORT_SYMBOL_GPL(fwnode_handle_put);
/**
* fwnode_device_is_available - check if a device is available for use
* @fwnode: Pointer to the fwnode of the device.
+ *
+ * For fwnode node types that don't implement the .device_is_available()
+ * operation, this function returns true.
*/
bool fwnode_device_is_available(const struct fwnode_handle *fwnode)
{
+ if (!fwnode_has_op(fwnode, device_is_available))
+ return true;
+
return fwnode_call_bool_op(fwnode, device_is_available);
}
EXPORT_SYMBOL_GPL(fwnode_device_is_available);
--
2.31.1
From 2295235fb4009d54dae3c2b5d5bed2f071cc9adc Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 21 Nov 2020 22:06:38 +0000
Subject: [PATCH] device property: Call fwnode_graph_get_endpoint_by_id() for
fwnode->secondary
This function is used to find fwnode endpoints against a device. In
some instances those endpoints are software nodes which are children of
fwnode->secondary. Add support to fwnode_graph_get_endpoint_by_id() to
find those endpoints by recursively calling itself passing the ptr to
fwnode->secondary in the event no endpoint is found for the primary.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/property.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 0bf5260f14c6..1421e9548857 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -1215,7 +1215,14 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode,
best_ep_id = fwnode_ep.id;
}
- return best_ep;
+ if (best_ep)
+ return best_ep;
+
+ if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary))
+ return fwnode_graph_get_endpoint_by_id(fwnode->secondary, port,
+ endpoint, flags);
+
+ return NULL;
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_endpoint_by_id);
--
2.31.1
From a3fd73a31c9fd72320ea18e18bb844f3caae5ff6 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 25 Oct 2020 22:49:08 +0000
Subject: [PATCH] software_node: Enforce parent before child ordering of nodes
arrays
Registering software_nodes with the .parent member set to point to a
currently unregistered software_node has the potential for problems,
so enforce parent -> child ordering in arrays passed in to
software_node_register_nodes().
Software nodes that are children of another software node should be
unregistered before their parent. To allow easy unregistering of an array
of software_nodes ordered parent to child, reverse the order in which
software_node_unregister_nodes() unregisters software_nodes.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index fbfb01ff1856..edfdd67daccd 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -692,7 +692,11 @@ swnode_register(const struct software_node *node, struct swnode *parent,
* software_node_register_nodes - Register an array of software nodes
* @nodes: Zero terminated array of software nodes to be registered
*
- * Register multiple software nodes at once.
+ * Register multiple software nodes at once. If any node in the array
+ * has its .parent pointer set (which can only be to another software_node),
+ * then its parent **must** have been registered before it is; either outside
+ * of this function or by ordering the array such that parent comes before
+ * child.
*/
int software_node_register_nodes(const struct software_node *nodes)
{
@@ -700,14 +704,23 @@ int software_node_register_nodes(const struct software_node *nodes)
int i;
for (i = 0; nodes[i].name; i++) {
- ret = software_node_register(&nodes[i]);
- if (ret) {
- software_node_unregister_nodes(nodes);
- return ret;
+ const struct software_node *parent = nodes[i].parent;
+
+ if (parent && !software_node_to_swnode(parent)) {
+ ret = -EINVAL;
+ goto err_unregister_nodes;
}
+
+ ret = software_node_register(&nodes[i]);
+ if (ret)
+ goto err_unregister_nodes;
}
return 0;
+
+err_unregister_nodes:
+ software_node_unregister_nodes(nodes);
+ return ret;
}
EXPORT_SYMBOL_GPL(software_node_register_nodes);
@@ -715,18 +728,23 @@ EXPORT_SYMBOL_GPL(software_node_register_nodes);
* software_node_unregister_nodes - Unregister an array of software nodes
* @nodes: Zero terminated array of software nodes to be unregistered
*
- * Unregister multiple software nodes at once.
+ * Unregister multiple software nodes at once. If parent pointers are set up
+ * in any of the software nodes then the array **must** be ordered such that
+ * parents come before their children.
*
- * NOTE: Be careful using this call if the nodes had parent pointers set up in
- * them before registering. If so, it is wiser to remove the nodes
- * individually, in the correct order (child before parent) instead of relying
- * on the sequential order of the list of nodes in the array.
+ * NOTE: If you are uncertain whether the array is ordered such that
+ * parents will be unregistered before their children, it is wiser to
+ * remove the nodes individually, in the correct order (child before
+ * parent).
*/
void software_node_unregister_nodes(const struct software_node *nodes)
{
- int i;
+ unsigned int i = 0;
+
+ while (nodes[i].name)
+ i++;
- for (i = 0; nodes[i].name; i++)
+ while (i--)
software_node_unregister(&nodes[i]);
}
EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
--
2.31.1
From 6702197b187628686cef125435d5090a8363b20f Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 22:25:03 +0100
Subject: [PATCH] software_node: unregister software_nodes in reverse order
To maintain consistency with software_node_unregister_nodes(), reverse
the order in which the software_node_unregister_node_group() function
unregisters nodes.
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index edfdd67daccd..b22290106284 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -779,16 +779,23 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group);
* software_node_unregister_node_group - Unregister a group of software nodes
* @node_group: NULL terminated array of software node pointers to be unregistered
*
- * Unregister multiple software nodes at once.
+ * Unregister multiple software nodes at once. The array will be unwound in
+ * reverse order (i.e. last entry first) and thus if any members of the array are
+ * children of another member then the children must appear later in the list such
+ * that they are unregistered first.
*/
-void software_node_unregister_node_group(const struct software_node **node_group)
+void software_node_unregister_node_group(
+ const struct software_node **node_group)
{
- unsigned int i;
+ unsigned int i = 0;
if (!node_group)
return;
- for (i = 0; node_group[i]; i++)
+ while (node_group[i])
+ i++;
+
+ while (i--)
software_node_unregister(node_group[i]);
}
EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
--
2.31.1
From 396ec69ecdab90bc93aa31c24dc5b6766ae68d62 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Tue, 22 Dec 2020 13:09:05 +0000
Subject: [PATCH] device property: Define format macros for ports and endpoints
OF, ACPI and software_nodes all implement graphs including nodes for ports
and endpoints. These are all intended to be named with a common schema,
as "port@n" and "endpoint@n" where n is an unsigned int representing the
index of the node. To ensure commonality across the subsystems, provide a
set of macros to define the format.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
include/linux/fwnode.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index fde4ad97564c..77414e431e89 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -50,6 +50,13 @@ struct fwnode_endpoint {
const struct fwnode_handle *local_fwnode;
};
+/*
+ * ports and endpoints defined as software_nodes should all follow a common
+ * naming scheme; use these macros to ensure commonality.
+ */
+#define SWNODE_GRAPH_PORT_NAME_FMT "port@%u"
+#define SWNODE_GRAPH_ENDPOINT_NAME_FMT "endpoint@%u"
+
#define NR_FWNODE_REFERENCE_ARGS 8
/**
--
2.31.1
From 7884439a1c8a217ae5cb246f2d4577ae43351ca2 Mon Sep 17 00:00:00 2001
From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Date: Tue, 15 Sep 2020 15:47:46 +0100
Subject: [PATCH] software_node: Add support for fwnode_graph*() family of
functions
This implements the remaining .graph_*() callbacks in the fwnode
operations structure for the software nodes. That makes the
fwnode_graph_*() functions available in the drivers also when software
nodes are used.
The implementation tries to mimic the "OF graph" as much as possible, but
there is no support for the "reg" device property. The ports will need to
have the index in their name which starts with "port@" (for example
"port@0", "port@1", ...) and endpoints will use the index of the software
node that is given to them during creation. The port nodes can also be
grouped under a specially named "ports" subnode, just like in DT, if
necessary.
The remote-endpoints are reference properties under the endpoint nodes
that are named "remote-endpoint".
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Co-developed-by: Daniel Scally <djrscally@gmail.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/base/swnode.c | 115 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 1 deletion(-)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index b22290106284..0e90bbf6e08c 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -540,6 +540,115 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
return 0;
}
+static struct fwnode_handle *
+swnode_graph_find_next_port(const struct fwnode_handle *parent,
+ struct fwnode_handle *port)
+{
+ struct fwnode_handle *old = port;
+
+ while ((port = software_node_get_next_child(parent, old))) {
+ /*
+ * fwnode ports have naming style "port@", so we search for any
+ * children that follow that convention.
+ */
+ if (!strncmp(to_swnode(port)->node->name, "port@",
+ strlen("port@")))
+ return port;
+ old = port;
+ }
+
+ return NULL;
+}
+
+static struct fwnode_handle *
+software_node_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
+ struct fwnode_handle *endpoint)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ struct fwnode_handle *parent;
+ struct fwnode_handle *port;
+
+ if (!swnode)
+ return NULL;
+
+ if (endpoint) {
+ port = software_node_get_parent(endpoint);
+ parent = software_node_get_parent(port);
+ } else {
+ parent = software_node_get_named_child_node(fwnode, "ports");
+ if (!parent)
+ parent = software_node_get(&swnode->fwnode);
+
+ port = swnode_graph_find_next_port(parent, NULL);
+ }
+
+ for (; port; port = swnode_graph_find_next_port(parent, port)) {
+ endpoint = software_node_get_next_child(port, endpoint);
+ if (endpoint) {
+ fwnode_handle_put(port);
+ break;
+ }
+ }
+
+ fwnode_handle_put(parent);
+
+ return endpoint;
+}
+
+static struct fwnode_handle *
+software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ const struct software_node_ref_args *ref;
+ const struct property_entry *prop;
+
+ if (!swnode)
+ return NULL;
+
+ prop = property_entry_get(swnode->node->properties, "remote-endpoint");
+ if (!prop || prop->type != DEV_PROP_REF || prop->is_inline)
+ return NULL;
+
+ ref = prop->pointer;
+
+ return software_node_get(software_node_fwnode(ref[0].node));
+}
+
+static struct fwnode_handle *
+software_node_graph_get_port_parent(struct fwnode_handle *fwnode)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+
+ swnode = swnode->parent;
+ if (swnode && !strcmp(swnode->node->name, "ports"))
+ swnode = swnode->parent;
+
+ return swnode ? software_node_get(&swnode->fwnode) : NULL;
+}
+
+static int
+software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
+ struct fwnode_endpoint *endpoint)
+{
+ struct swnode *swnode = to_swnode(fwnode);
+ const char *parent_name = swnode->parent->node->name;
+ int ret;
+
+ if (strlen("port@") >= strlen(parent_name) ||
+ strncmp(parent_name, "port@", strlen("port@")))
+ return -EINVAL;
+
+ /* Ports have naming style "port@n", we need to select the n */
+ ret = kstrtou32(parent_name + strlen("port@"), 10, &endpoint->port);
+ if (ret)
+ return ret;
+
+ endpoint->id = swnode->id;
+ endpoint->local_fwnode = fwnode;
+
+ return 0;
+}
+
static const struct fwnode_operations software_node_ops = {
.get = software_node_get,
.put = software_node_put,
@@ -551,7 +660,11 @@ static const struct fwnode_operations software_node_ops = {
.get_parent = software_node_get_parent,
.get_next_child_node = software_node_get_next_child,
.get_named_child_node = software_node_get_named_child_node,
- .get_reference_args = software_node_get_reference_args
+ .get_reference_args = software_node_get_reference_args,
+ .graph_get_next_endpoint = software_node_graph_get_next_endpoint,
+ .graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
+ .graph_get_port_parent = software_node_graph_get_port_parent,
+ .graph_parse_endpoint = software_node_graph_parse_endpoint,
};
/* -------------------------------------------------------------------------- */
--
2.31.1
From 52ca4770dc6d01fb95727ea42acd2f86ed33eabf Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 23:07:22 +0100
Subject: [PATCH] lib/test_printf.c: Use helper function to unwind array of
software_nodes
Use the software_node_unregister_nodes() helper function to unwind this
array in a cleaner way.
Acked-by: Petr Mladek <pmladek@suse.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
lib/test_printf.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lib/test_printf.c b/lib/test_printf.c
index 7ac87f18a10f..7d60f24240a4 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -644,9 +644,7 @@ static void __init fwnode_pointer(void)
test(second_name, "%pfwP", software_node_fwnode(&softnodes[1]));
test(third_name, "%pfwP", software_node_fwnode(&softnodes[2]));
- software_node_unregister(&softnodes[2]);
- software_node_unregister(&softnodes[1]);
- software_node_unregister(&softnodes[0]);
+ software_node_unregister_nodes(softnodes);
}
static void __init
--
2.31.1
From 035dc1125532aa59d97d2a96642f62da2f7eb80b Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 23:11:36 +0100
Subject: [PATCH] ipu3-cio2: Add T: entry to MAINTAINERS
Development for the ipu3-cio2 driver is taking place in media_tree, but
there's no T: entry in MAINTAINERS to denote that - rectify that oversight
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d1b36e222cd1..c6c13433ecf6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9009,6 +9009,7 @@ M: Bingbu Cao <bingbu.cao@intel.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
+T: git git://linuxtv.org/media_tree.git
F: Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst
F: drivers/media/pci/intel/ipu3/
--
2.31.1
From bde2d98ef70af9d8d47fe654af8f3d671678c0ad Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 10 Oct 2020 22:47:21 +0100
Subject: [PATCH] ipu3-cio2: Rename ipu3-cio2.c
ipu3-cio2 driver needs extending with multiple files; rename the main
source file and specify the renamed file in Makefile to accommodate that.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/pci/intel/ipu3/Makefile | 2 ++
drivers/media/pci/intel/ipu3/{ipu3-cio2.c => ipu3-cio2-main.c} | 0
2 files changed, 2 insertions(+)
rename drivers/media/pci/intel/ipu3/{ipu3-cio2.c => ipu3-cio2-main.c} (100%)
diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
index 98ddd5beafe0..429d516452e4 100644
--- a/drivers/media/pci/intel/ipu3/Makefile
+++ b/drivers/media/pci/intel/ipu3/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
+
+ipu3-cio2-y += ipu3-cio2-main.o
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
similarity index 100%
rename from drivers/media/pci/intel/ipu3/ipu3-cio2.c
rename to drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
--
2.31.1
From 0f89c00a20fec334b3e016d4f4ac960a967ba9ca Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 21:53:05 +0100
Subject: [PATCH] media: v4l2-core: v4l2-async: Check sd->fwnode->secondary in
match_fwnode()
Where the fwnode graph is comprised of software_nodes, these will be
assigned as the secondary to dev->fwnode. Check the v4l2_subdev's fwnode
for a secondary and attempt to match against it during match_fwnode() to
accommodate that possibility.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/v4l2-core/v4l2-async.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index e3ab003a6c85..9dd896d085ec 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -87,6 +87,14 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier,
if (sd->fwnode == asd->match.fwnode)
return true;
+ /*
+ * Check the same situation for any possible secondary assigned to the
+ * subdev's fwnode
+ */
+ if (!IS_ERR_OR_NULL(sd->fwnode->secondary) &&
+ sd->fwnode->secondary == asd->match.fwnode)
+ return true;
+
/*
* Otherwise, check if the sd fwnode and the asd fwnode refer to an
* endpoint or a device. If they're of the same type, there's no match.
--
2.31.1
From e3d0e2a69393cb9ba5e98a1dad1c71078ea3f669 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 15 Nov 2020 08:15:34 +0000
Subject: [PATCH] ACPI / bus: Add acpi_dev_get_next_match_dev() and helper
macro
To ensure we handle situations in which multiple sensors of the same
model (and therefore _HID) are present in a system, we need to be able
to iterate over devices matching a known _HID but unknown _UID and _HRV
- add acpi_dev_get_next_match_dev() to accommodate that possibility and
change acpi_dev_get_first_match_dev() to simply call the new function
with a NULL starting point. Add an iterator macro for convenience.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/acpi/utils.c | 30 ++++++++++++++++++++++++++----
include/acpi/acpi_bus.h | 7 +++++++
2 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index d5411a166685..ddca1550cce6 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -843,12 +843,13 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
EXPORT_SYMBOL(acpi_dev_present);
/**
- * acpi_dev_get_first_match_dev - Return the first match of ACPI device
+ * acpi_dev_get_next_match_dev - Return the next match of ACPI device
+ * @adev: Pointer to the previous acpi_device matching this @hid, @uid and @hrv
* @hid: Hardware ID of the device.
* @uid: Unique ID of the device, pass NULL to not check _UID
* @hrv: Hardware Revision of the device, pass -1 to not check _HRV
*
- * Return the first match of ACPI device if a matching device was present
+ * Return the next match of ACPI device if another matching device was present
* at the moment of invocation, or NULL otherwise.
*
* The caller is responsible to call put_device() on the returned device.
@@ -856,8 +857,9 @@ EXPORT_SYMBOL(acpi_dev_present);
* See additional information in acpi_dev_present() as well.
*/
struct acpi_device *
-acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
+acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv)
{
+ struct device *start = adev ? &adev->dev : NULL;
struct acpi_dev_match_info match = {};
struct device *dev;
@@ -865,9 +867,29 @@ acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
match.uid = uid;
match.hrv = hrv;
- dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
+ dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb);
return dev ? to_acpi_device(dev) : NULL;
}
+EXPORT_SYMBOL(acpi_dev_get_next_match_dev);
+
+/**
+ * acpi_dev_get_first_match_dev - Return the first match of ACPI device
+ * @hid: Hardware ID of the device.
+ * @uid: Unique ID of the device, pass NULL to not check _UID
+ * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
+ *
+ * Return the first match of ACPI device if a matching device was present
+ * at the moment of invocation, or NULL otherwise.
+ *
+ * The caller is responsible to call put_device() on the returned device.
+ *
+ * See additional information in acpi_dev_present() as well.
+ */
+struct acpi_device *
+acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
+{
+ return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv);
+}
EXPORT_SYMBOL(acpi_dev_get_first_match_dev);
/*
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 37dac195adbb..f28b097c658f 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -684,9 +684,16 @@ 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);
+struct acpi_device *
+acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv);
struct acpi_device *
acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv);
+#define for_each_acpi_dev_match(adev, hid, uid, hrv) \
+ for (adev = acpi_dev_get_first_match_dev(hid, uid, hrv); \
+ adev; \
+ adev = acpi_dev_get_next_match_dev(adev, hid, uid, hrv))
+
static inline void acpi_dev_put(struct acpi_device *adev)
{
put_device(&adev->dev);
--
2.31.1
From 821d8607d78a340e6e196d354a62f3793b02a8d9 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 19 Dec 2020 23:55:04 +0000
Subject: [PATCH] media: v4l2-fwnode: Include v4l2_fwnode_bus_type
V4L2 fwnode bus types are enumerated in v4l2-fwnode.c, meaning they aren't
available to the rest of the kernel. Move the enum to the corresponding
header so that I can use the label to refer to those values.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/v4l2-core/v4l2-fwnode.c | 11 -----------
include/media/v4l2-fwnode.h | 22 ++++++++++++++++++++++
2 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 5353e37eb950..c1c2b3060532 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -28,17 +28,6 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
-enum v4l2_fwnode_bus_type {
- V4L2_FWNODE_BUS_TYPE_GUESS = 0,
- V4L2_FWNODE_BUS_TYPE_CSI2_CPHY,
- V4L2_FWNODE_BUS_TYPE_CSI1,
- V4L2_FWNODE_BUS_TYPE_CCP2,
- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY,
- V4L2_FWNODE_BUS_TYPE_PARALLEL,
- V4L2_FWNODE_BUS_TYPE_BT656,
- NR_OF_V4L2_FWNODE_BUS_TYPE,
-};
-
static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type;
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 4365430eea6f..77fd6a3ec308 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -213,6 +213,28 @@ struct v4l2_fwnode_connector {
} connector;
};
+/**
+ * enum v4l2_fwnode_bus_type - Video bus types defined by firmware properties
+ * @V4L2_FWNODE_BUS_TYPE_GUESS: Default value if no bus-type fwnode property
+ * @V4L2_FWNODE_BUS_TYPE_CSI2_CPHY: MIPI CSI-2 bus, C-PHY physical layer
+ * @V4L2_FWNODE_BUS_TYPE_CSI1: MIPI CSI-1 bus
+ * @V4L2_FWNODE_BUS_TYPE_CCP2: SMIA Compact Camera Port 2 bus
+ * @V4L2_FWNODE_BUS_TYPE_CSI2_DPHY: MIPI CSI-2 bus, D-PHY physical layer
+ * @V4L2_FWNODE_BUS_TYPE_PARALLEL: Camera Parallel Interface bus
+ * @V4L2_FWNODE_BUS_TYPE_BT656: BT.656 video format bus-type
+ * @NR_OF_V4L2_FWNODE_BUS_TYPE: Number of bus-types
+ */
+enum v4l2_fwnode_bus_type {
+ V4L2_FWNODE_BUS_TYPE_GUESS = 0,
+ V4L2_FWNODE_BUS_TYPE_CSI2_CPHY,
+ V4L2_FWNODE_BUS_TYPE_CSI1,
+ V4L2_FWNODE_BUS_TYPE_CCP2,
+ V4L2_FWNODE_BUS_TYPE_CSI2_DPHY,
+ V4L2_FWNODE_BUS_TYPE_PARALLEL,
+ V4L2_FWNODE_BUS_TYPE_BT656,
+ NR_OF_V4L2_FWNODE_BUS_TYPE
+};
+
/**
* v4l2_fwnode_endpoint_parse() - parse all fwnode node properties
* @fwnode: pointer to the endpoint's fwnode handle
--
2.31.1
From 30287cf2c0dbf32bb95cb8ef77b0e11b11a98e79 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 21 Oct 2020 21:53:44 +0100
Subject: [PATCH] ipu3-cio2: Add cio2-bridge to ipu3-cio2 driver
Currently on platforms designed for Windows, connections between CIO2 and
sensors are not properly defined in DSDT. This patch extends the ipu3-cio2
driver to compensate by building software_node connections, parsing the
connection properties from the sensor's SSDB buffer.
Suggested-by: Jordan Hand <jorhand@linux.microsoft.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
MAINTAINERS | 1 +
drivers/media/pci/intel/ipu3/Kconfig | 18 +
drivers/media/pci/intel/ipu3/Makefile | 1 +
drivers/media/pci/intel/ipu3/cio2-bridge.c | 311 ++++++++++++++++++
drivers/media/pci/intel/ipu3/cio2-bridge.h | 125 +++++++
drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 34 ++
drivers/media/pci/intel/ipu3/ipu3-cio2.h | 6 +
7 files changed, 496 insertions(+)
create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.c
create mode 100644 drivers/media/pci/intel/ipu3/cio2-bridge.h
diff --git a/MAINTAINERS b/MAINTAINERS
index c6c13433ecf6..1bade5b42a40 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9006,6 +9006,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
M: Yong Zhi <yong.zhi@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
M: Bingbu Cao <bingbu.cao@intel.com>
+M: Dan Scally <djrscally@gmail.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
index 7a805201034b..24f4e79fe0cb 100644
--- a/drivers/media/pci/intel/ipu3/Kconfig
+++ b/drivers/media/pci/intel/ipu3/Kconfig
@@ -17,3 +17,21 @@ config VIDEO_IPU3_CIO2
Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
connected camera.
The module will be called ipu3-cio2.
+
+config CIO2_BRIDGE
+ bool "IPU3 CIO2 Sensors Bridge"
+ depends on VIDEO_IPU3_CIO2
+ help
+ This extension provides an API for the ipu3-cio2 driver to create
+ connections to cameras that are hidden in the SSDB buffer in ACPI.
+ It can be used to enable support for cameras in detachable / hybrid
+ devices that ship with Windows.
+
+ Say Y here if your device is a detachable / hybrid laptop that comes
+ with Windows installed by the OEM, for example:
+
+ - Microsoft Surface models (except Surface Pro 3)
+ - The Lenovo Miix line (for example the 510, 520, 710 and 720)
+ - Dell 7285
+
+ If in doubt, say N here.
diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile
index 429d516452e4..933777e6ea8a 100644
--- a/drivers/media/pci/intel/ipu3/Makefile
+++ b/drivers/media/pci/intel/ipu3/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o
ipu3-cio2-y += ipu3-cio2-main.o
+ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c
new file mode 100644
index 000000000000..143f3c0f445e
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/property.h>
+#include <media/v4l2-fwnode.h>
+
+#include "cio2-bridge.h"
+
+/*
+ * Extend this array with ACPI Hardware IDs of devices known to be working
+ * plus the number of link-frequencies expected by their drivers, along with
+ * the frequency values in hertz. This is somewhat opportunistic way of adding
+ * support for this for now in the hopes of a better source for the information
+ * (possibly some encoded value in the SSDB buffer that we're unaware of)
+ * becoming apparent in the future.
+ *
+ * Do not add an entry for a sensor that is not actually supported.
+ */
+static const struct cio2_sensor_config cio2_supported_sensors[] = {
+ /* Omnivision OV5693 */
+ CIO2_SENSOR_CONFIG("INT33BE", 0),
+ /* Omnivision OV2680 */
+ CIO2_SENSOR_CONFIG("OVTI2680", 0),
+};
+
+static const struct cio2_property_names prop_names = {
+ .clock_frequency = "clock-frequency",
+ .rotation = "rotation",
+ .bus_type = "bus-type",
+ .data_lanes = "data-lanes",
+ .remote_endpoint = "remote-endpoint",
+ .link_frequencies = "link-frequencies",
+};
+
+static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id,
+ void *data, u32 size)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = 0;
+
+ status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ obj = buffer.pointer;
+ if (!obj) {
+ dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
+ return -ENODEV;
+ }
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_err(&adev->dev, "Not an ACPI buffer\n");
+ ret = -ENODEV;
+ goto out_free_buff;
+ }
+
+ if (obj->buffer.length > size) {
+ dev_err(&adev->dev, "Given buffer is too small\n");
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ memcpy(data, obj->buffer.pointer, obj->buffer.length);
+
+out_free_buff:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static void cio2_bridge_create_fwnode_properties(
+ struct cio2_sensor *sensor,
+ struct cio2_bridge *bridge,
+ const struct cio2_sensor_config *cfg)
+{
+ sensor->prop_names = prop_names;
+
+ sensor->local_ref[0].node = &sensor->swnodes[SWNODE_CIO2_ENDPOINT];
+ sensor->remote_ref[0].node = &sensor->swnodes[SWNODE_SENSOR_ENDPOINT];
+
+ sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
+ sensor->prop_names.clock_frequency,
+ sensor->ssdb.mclkspeed);
+ sensor->dev_properties[1] = PROPERTY_ENTRY_U8(
+ sensor->prop_names.rotation,
+ sensor->ssdb.degree);
+
+ sensor->ep_properties[0] = PROPERTY_ENTRY_U32(
+ sensor->prop_names.bus_type,
+ V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
+ sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(
+ sensor->prop_names.data_lanes,
+ bridge->data_lanes,
+ sensor->ssdb.lanes);
+ sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(
+ sensor->prop_names.remote_endpoint,
+ sensor->local_ref);
+
+ if (cfg->nr_link_freqs > 0)
+ sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
+ sensor->prop_names.link_frequencies,
+ cfg->link_freqs,
+ cfg->nr_link_freqs);
+
+ sensor->cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
+ sensor->prop_names.data_lanes,
+ bridge->data_lanes,
+ sensor->ssdb.lanes);
+ sensor->cio2_properties[1] = PROPERTY_ENTRY_REF_ARRAY(
+ sensor->prop_names.remote_endpoint,
+ sensor->remote_ref);
+}
+
+static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor)
+{
+ snprintf(sensor->node_names.remote_port,
+ sizeof(sensor->node_names.remote_port),
+ SWNODE_GRAPH_PORT_NAME_FMT, sensor->ssdb.link);
+ snprintf(sensor->node_names.port,
+ sizeof(sensor->node_names.port),
+ SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */
+ snprintf(sensor->node_names.endpoint,
+ sizeof(sensor->node_names.endpoint),
+ SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */
+}
+
+static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge,
+ struct cio2_sensor *sensor)
+{
+ struct software_node *nodes = sensor->swnodes;
+
+ cio2_bridge_init_swnode_names(sensor);
+
+ nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name,
+ sensor->dev_properties);
+ nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port,
+ &nodes[SWNODE_SENSOR_HID]);
+ nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT(
+ sensor->node_names.endpoint,
+ &nodes[SWNODE_SENSOR_PORT],
+ sensor->ep_properties);
+ nodes[SWNODE_CIO2_PORT] = NODE_PORT(sensor->node_names.remote_port,
+ &bridge->cio2_hid_node);
+ nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT(
+ sensor->node_names.endpoint,
+ &nodes[SWNODE_CIO2_PORT],
+ sensor->cio2_properties);
+}
+
+static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge)
+{
+ struct cio2_sensor *sensor;
+ unsigned int i;
+
+ for (i = 0; i < bridge->n_sensors; i++) {
+ sensor = &bridge->sensors[i];
+ software_node_unregister_nodes(sensor->swnodes);
+ acpi_dev_put(sensor->adev);
+ }
+}
+
+static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg,
+ struct cio2_bridge *bridge,
+ struct pci_dev *cio2)
+{
+ struct fwnode_handle *fwnode;
+ struct cio2_sensor *sensor;
+ struct acpi_device *adev;
+ int ret;
+
+ for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
+ if (!adev->status.enabled)
+ continue;
+
+ if (bridge->n_sensors >= CIO2_NUM_PORTS) {
+ dev_err(&cio2->dev, "Exceeded available CIO2 ports\n");
+ cio2_bridge_unregister_sensors(bridge);
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ sensor = &bridge->sensors[bridge->n_sensors];
+ sensor->adev = adev;
+ strscpy(sensor->name, cfg->hid, sizeof(sensor->name));
+
+ ret = cio2_bridge_read_acpi_buffer(adev, "SSDB",
+ &sensor->ssdb,
+ sizeof(sensor->ssdb));
+ if (ret)
+ 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;
+ }
+
+ cio2_bridge_create_fwnode_properties(sensor, bridge, cfg);
+ cio2_bridge_create_connection_swnodes(bridge, sensor);
+
+ ret = software_node_register_nodes(sensor->swnodes);
+ if (ret)
+ goto err_put_adev;
+
+ fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR_HID]);
+ if (!fwnode) {
+ ret = -ENODEV;
+ goto err_free_swnodes;
+ }
+
+ adev->fwnode.secondary = fwnode;
+
+ dev_info(&cio2->dev, "Found supported sensor %s\n",
+ acpi_dev_name(adev));
+
+ bridge->n_sensors++;
+ }
+
+ return 0;
+
+err_free_swnodes:
+ software_node_unregister_nodes(sensor->swnodes);
+err_put_adev:
+ acpi_dev_put(sensor->adev);
+err_out:
+ return ret;
+}
+
+static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge,
+ struct pci_dev *cio2)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(cio2_supported_sensors); i++) {
+ const struct cio2_sensor_config *cfg = &cio2_supported_sensors[i];
+
+ ret = cio2_bridge_connect_sensor(cfg, bridge, cio2);
+ if (ret)
+ goto err_unregister_sensors;
+ }
+
+ return 0;
+
+err_unregister_sensors:
+ cio2_bridge_unregister_sensors(bridge);
+ return ret;
+}
+
+int cio2_bridge_init(struct pci_dev *cio2)
+{
+ struct device *dev = &cio2->dev;
+ struct fwnode_handle *fwnode;
+ struct cio2_bridge *bridge;
+ unsigned int i;
+ int ret;
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
+
+ strscpy(bridge->cio2_node_name, CIO2_HID, sizeof(bridge->cio2_node_name));
+ bridge->cio2_hid_node.name = bridge->cio2_node_name;
+
+ ret = software_node_register(&bridge->cio2_hid_node);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register the CIO2 HID node\n");
+ goto err_free_bridge;
+ }
+
+ /*
+ * Map the lane arrangement, which is fixed for the IPU3 (meaning we
+ * only need one, rather than one per sensor). We include it as a
+ * member of the struct cio2_bridge rather than a global variable so
+ * that it survives if the module is unloaded along with the rest of
+ * the struct.
+ */
+ for (i = 0; i < CIO2_MAX_LANES; i++)
+ bridge->data_lanes[i] = i + 1;
+
+ ret = cio2_bridge_connect_sensors(bridge, cio2);
+ if (ret || bridge->n_sensors == 0)
+ goto err_unregister_cio2;
+
+ dev_info(dev, "Connected %d cameras\n", bridge->n_sensors);
+
+ fwnode = software_node_fwnode(&bridge->cio2_hid_node);
+ if (!fwnode) {
+ dev_err(dev, "Error getting fwnode from cio2 software_node\n");
+ ret = -ENODEV;
+ goto err_unregister_sensors;
+ }
+
+ set_secondary_fwnode(dev, fwnode);
+
+ return 0;
+
+err_unregister_sensors:
+ cio2_bridge_unregister_sensors(bridge);
+err_unregister_cio2:
+ software_node_unregister(&bridge->cio2_hid_node);
+err_free_bridge:
+ kfree(bridge);
+
+ return ret;
+}
diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/drivers/media/pci/intel/ipu3/cio2-bridge.h
new file mode 100644
index 000000000000..dd0ffcafa489
--- /dev/null
+++ b/drivers/media/pci/intel/ipu3/cio2-bridge.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+#ifndef __CIO2_BRIDGE_H
+#define __CIO2_BRIDGE_H
+
+#include <linux/property.h>
+#include <linux/types.h>
+
+#include "ipu3-cio2.h"
+
+#define CIO2_HID "INT343E"
+#define CIO2_MAX_LANES 4
+#define MAX_NUM_LINK_FREQS 3
+
+#define CIO2_SENSOR_CONFIG(_HID, _NR, ...) \
+ (const struct cio2_sensor_config) { \
+ .hid = _HID, \
+ .nr_link_freqs = _NR, \
+ .link_freqs = { __VA_ARGS__ } \
+ }
+
+#define NODE_SENSOR(_HID, _PROPS) \
+ (const struct software_node) { \
+ .name = _HID, \
+ .properties = _PROPS, \
+ }
+
+#define NODE_PORT(_PORT, _SENSOR_NODE) \
+ (const struct software_node) { \
+ .name = _PORT, \
+ .parent = _SENSOR_NODE, \
+ }
+
+#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \
+ (const struct software_node) { \
+ .name = _EP, \
+ .parent = _PORT, \
+ .properties = _PROPS, \
+ }
+
+enum cio2_sensor_swnodes {
+ SWNODE_SENSOR_HID,
+ SWNODE_SENSOR_PORT,
+ SWNODE_SENSOR_ENDPOINT,
+ SWNODE_CIO2_PORT,
+ SWNODE_CIO2_ENDPOINT,
+ SWNODE_COUNT
+};
+
+/* Data representation as it is in ACPI SSDB buffer */
+struct cio2_sensor_ssdb {
+ u8 version;
+ u8 sku;
+ u8 guid_csi2[16];
+ u8 devfunction;
+ u8 bus;
+ u32 dphylinkenfuses;
+ u32 clockdiv;
+ u8 link;
+ u8 lanes;
+ u32 csiparams[10];
+ u32 maxlanespeed;
+ u8 sensorcalibfileidx;
+ u8 sensorcalibfileidxInMBZ[3];
+ u8 romtype;
+ u8 vcmtype;
+ u8 platforminfo;
+ u8 platformsubinfo;
+ u8 flash;
+ u8 privacyled;
+ u8 degree;
+ u8 mipilinkdefined;
+ u32 mclkspeed;
+ u8 controllogicid;
+ u8 reserved1[3];
+ u8 mclkport;
+ u8 reserved2[13];
+} __packed;
+
+struct cio2_property_names {
+ char clock_frequency[16];
+ char rotation[9];
+ char bus_type[9];
+ char data_lanes[11];
+ char remote_endpoint[16];
+ char link_frequencies[17];
+};
+
+struct cio2_node_names {
+ char port[7];
+ char endpoint[11];
+ char remote_port[7];
+};
+
+struct cio2_sensor_config {
+ const char *hid;
+ const u8 nr_link_freqs;
+ const u64 link_freqs[MAX_NUM_LINK_FREQS];
+};
+
+struct cio2_sensor {
+ char name[ACPI_ID_LEN];
+ struct acpi_device *adev;
+
+ struct software_node swnodes[6];
+ struct cio2_node_names node_names;
+
+ struct cio2_sensor_ssdb ssdb;
+ struct cio2_property_names prop_names;
+ struct property_entry ep_properties[5];
+ struct property_entry dev_properties[3];
+ struct property_entry cio2_properties[3];
+ struct software_node_ref_args local_ref[1];
+ struct software_node_ref_args remote_ref[1];
+};
+
+struct cio2_bridge {
+ char cio2_node_name[ACPI_ID_LEN];
+ struct software_node cio2_hid_node;
+ u32 data_lanes[4];
+ unsigned int n_sensors;
+ struct cio2_sensor sensors[CIO2_NUM_PORTS];
+};
+
+#endif
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
index 325c1483f42b..5e0a449fe2bc 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c
@@ -1702,11 +1702,28 @@ static void cio2_queues_exit(struct cio2_device *cio2)
cio2_queue_exit(cio2, &cio2->queue[i]);
}
+static int cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *endpoint;
+
+ if (IS_ERR_OR_NULL(fwnode))
+ return -EINVAL;
+
+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (endpoint) {
+ fwnode_handle_put(endpoint);
+ return 0;
+ }
+
+ return cio2_check_fwnode_graph(fwnode->secondary);
+}
+
/**************** PCI interface ****************/
static int cio2_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
+ struct fwnode_handle *fwnode = dev_fwnode(&pci_dev->dev);
struct cio2_device *cio2;
int r;
@@ -1715,6 +1732,23 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
return -ENOMEM;
cio2->pci_dev = pci_dev;
+ /*
+ * On some platforms no connections to sensors are defined in firmware,
+ * if the device has no endpoints then we can try to build those as
+ * software_nodes parsed from SSDB.
+ */
+ r = cio2_check_fwnode_graph(fwnode);
+ if (r) {
+ if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) {
+ dev_err(&pci_dev->dev, "fwnode graph has no endpoints connected\n");
+ return -EINVAL;
+ }
+
+ r = cio2_bridge_init(pci_dev);
+ if (r)
+ return r;
+ }
+
r = pcim_enable_device(pci_dev);
if (r) {
dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index 62187ab5ae43..dc3e343a37fb 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -455,4 +455,10 @@ static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq)
return container_of(vq, struct cio2_queue, vbq);
}
+#if IS_ENABLED(CONFIG_CIO2_BRIDGE)
+int cio2_bridge_init(struct pci_dev *cio2);
+#else
+int cio2_bridge_init(struct pci_dev *cio2) { return 0; }
+#endif
+
#endif
--
2.31.1
From 8be47a0765f714b6ae57f03ff7398a3c55ab1a0b Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 2 Dec 2020 12:38:10 +0000
Subject: [PATCH] acpi: utils: move acpi_lpss_dep() to utils
I need to be able to identify devices which declare themselves to be
dependent on other devices through _DEP; add this function to utils.c
and export it to the rest of the ACPI layer.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/acpi/acpi_lpss.c | 24 ------------------------
drivers/acpi/internal.h | 1 +
drivers/acpi/utils.c | 24 ++++++++++++++++++++++++
3 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index be73974ce449..70c7d9a3f715 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -543,30 +543,6 @@ static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
}
-static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
-{
- struct acpi_handle_list dep_devices;
- acpi_status status;
- int i;
-
- if (!acpi_has_method(adev->handle, "_DEP"))
- return false;
-
- status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
- &dep_devices);
- if (ACPI_FAILURE(status)) {
- dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
- return false;
- }
-
- for (i = 0; i < dep_devices.count; i++) {
- if (dep_devices.handles[i] == handle)
- return true;
- }
-
- return false;
-}
-
static void acpi_lpss_link_consumer(struct device *dev1,
const struct lpss_device_links *link)
{
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index cb8f70842249..a7051a944c26 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -81,6 +81,7 @@ static inline void acpi_lpss_init(void) {}
#endif
void acpi_apd_init(void);
+bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle);
acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
bool acpi_queue_hotplug_work(struct work_struct *work);
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index ddca1550cce6..78b38775f18b 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -807,6 +807,30 @@ static int acpi_dev_match_cb(struct device *dev, const void *data)
return hrv == match->hrv;
}
+bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
+{
+ struct acpi_handle_list dep_devices;
+ acpi_status status;
+ int i;
+
+ if (!acpi_has_method(adev->handle, "_DEP"))
+ return false;
+
+ status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
+ &dep_devices);
+ if (ACPI_FAILURE(status)) {
+ dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
+ return false;
+ }
+
+ for (i = 0; i < dep_devices.count; i++) {
+ if (dep_devices.handles[i] == handle)
+ return true;
+ }
+
+ return false;
+}
+
/**
* acpi_dev_present - Detect that a given ACPI device is present
* @hid: Hardware ID of the device.
--
2.31.1
From f3ba823775b88ccc7f65e17f768fbf47c04743ba Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 26 Nov 2020 21:12:41 +0000
Subject: [PATCH] acpi: utils: Add function to fetch dependent acpi_devices
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 function to parse all ACPI Devices and check if
the include the handle of the dependee device in their _DEP buffer.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/acpi/utils.c | 34 ++++++++++++++++++++++++++++++++++
include/acpi/acpi_bus.h | 2 ++
2 files changed, 36 insertions(+)
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 78b38775f18b..ec6a2406a886 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -831,6 +831,18 @@ bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
return false;
}
+static int acpi_dev_match_by_dep(struct device *dev, const void *data)
+{
+ struct acpi_device *adev = to_acpi_device(dev);
+ const struct acpi_device *dependee = data;
+ acpi_handle handle = dependee->handle;
+
+ if (acpi_lpss_dep(adev, handle))
+ return 1;
+
+ return 0;
+}
+
/**
* acpi_dev_present - Detect that a given ACPI device is present
* @hid: Hardware ID of the device.
@@ -866,6 +878,28 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
}
EXPORT_SYMBOL(acpi_dev_present);
+/**
+ * acpi_dev_get_next_dep_dev - Return next ACPI device dependent on input dev
+ * @adev: Pointer to the dependee device
+ * @prev: Pointer to the previous dependent device (or NULL for first match)
+ *
+ * Return the next ACPI device which declares itself dependent on @adev in
+ * the _DEP buffer.
+ *
+ * The caller is responsible to call put_device() on the returned device.
+ */
+struct acpi_device *acpi_dev_get_next_dep_dev(struct acpi_device *adev,
+ struct acpi_device *prev)
+{
+ struct device *start = prev ? &prev->dev : NULL;
+ struct device *dev;
+
+ dev = bus_find_device(&acpi_bus_type, start, adev, acpi_dev_match_by_dep);
+
+ return dev ? to_acpi_device(dev) : NULL;
+}
+EXPORT_SYMBOL(acpi_dev_get_next_dep_dev);
+
/**
* acpi_dev_get_next_match_dev - Return the next match of ACPI device
* @adev: Pointer to the previous acpi_device matching this @hid, @uid and @hrv
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index f28b097c658f..9bec3373f850 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -684,6 +684,8 @@ 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);
+struct acpi_device *
+acpi_dev_get_next_dep_dev(struct acpi_device *adev, struct acpi_device *prev);
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.31.1
From 9868b1f445f3016247e6995caa27347bebb5b0cf Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 16 Nov 2020 21:38:49 +0000
Subject: [PATCH] i2c: i2c-core-base: Use format macro in i2c_dev_set_name()
Some places in the kernel allow users to map resources to a device
using device name (for example, gpiod_lookup_table). Currently
this involves waiting for the i2c_client to have been registered so we
can use dev_name(&client->dev). We want to add a function to allow users
to refer to an i2c device by name before it has been instantiated, so
create a macro for the format that's accessible outside the i2c layer
and use it in i2c_dev_set_name()
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/i2c/i2c-core-base.c | 4 ++--
include/linux/i2c.h | 7 +++++++
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index f21362355973..e2cf16f27d65 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -812,12 +812,12 @@ static void i2c_dev_set_name(struct i2c_adapter *adap,
struct acpi_device *adev = ACPI_COMPANION(&client->dev);
if (info && info->dev_name) {
- dev_set_name(&client->dev, "i2c-%s", info->dev_name);
+ dev_set_name(&client->dev, I2C_DEV_NAME_FORMAT, info->dev_name);
return;
}
if (adev) {
- dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev));
+ dev_set_name(&client->dev, I2C_DEV_NAME_FORMAT, acpi_dev_name(adev));
return;
}
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index a670ae129f4b..b18172f240af 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -39,6 +39,9 @@ enum i2c_slave_event;
typedef int (*i2c_slave_cb_t)(struct i2c_client *client,
enum i2c_slave_event event, u8 *val);
+/* I2C Device Name Format - to maintain consistency outside the i2c layer */
+#define I2C_DEV_NAME_FORMAT "i2c-%s"
+
/* I2C Frequency Modes */
#define I2C_MAX_STANDARD_MODE_FREQ 100000
#define I2C_MAX_FAST_MODE_FREQ 400000
@@ -1013,6 +1016,10 @@ static inline struct i2c_client *i2c_acpi_new_device(struct device *dev,
{
return ERR_PTR(-ENODEV);
}
+static inline char *i2c_acpi_dev_name(struct acpi_device *adev)
+{
+ return NULL;
+}
static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle)
{
return NULL;
--
2.31.1
From 84a1cde3c9578bf6f4ced9bea6b3ff344ef363fc Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 2 Dec 2020 16:41:42 +0000
Subject: [PATCH] i2c: i2c-core-acpi: Add i2c_acpi_dev_name()
We want to refer to an i2c device by name before it has been
created by the kernel; add a function that constructs the name
from the acpi device instead.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/i2c/i2c-core-acpi.c | 16 ++++++++++++++++
include/linux/i2c.h | 1 +
2 files changed, 17 insertions(+)
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index aed579942436..89751415b69b 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -497,6 +497,22 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
}
EXPORT_SYMBOL_GPL(i2c_acpi_new_device);
+/**
+ * i2c_acpi_dev_name - Construct i2c device name for devs sourced from ACPI
+ * @adev: ACPI device to construct the name for
+ *
+ * Constructs the name of an i2c device matching the format used by
+ * i2c_dev_set_name() to allow users to refer to an i2c device by name even
+ * before they have been instantiated.
+ *
+ * The caller is responsible for freeing the returned pointer.
+ */
+char *i2c_acpi_dev_name(struct acpi_device *adev)
+{
+ return kasprintf(GFP_KERNEL, I2C_DEV_NAME_FORMAT, acpi_dev_name(adev));
+}
+EXPORT_SYMBOL_GPL(i2c_acpi_dev_name);
+
#ifdef CONFIG_ACPI_I2C_OPREGION
static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
u8 cmd, u8 *data, u8 data_len)
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index b18172f240af..269a2009080c 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -1000,6 +1000,7 @@ bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
u32 i2c_acpi_find_bus_speed(struct device *dev);
struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
struct i2c_board_info *info);
+char *i2c_acpi_dev_name(struct acpi_device *adev);
struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle);
#else
static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
--
2.31.1
From 88904c22d4cad03a741d4769d2d6a1241cea562d Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 16 Nov 2020 00:16:56 +0000
Subject: [PATCH] gpio: gpiolib-acpi: Export acpi_get_gpiod()
I need to be able to translate GPIO resources in an acpi_device's _CRS
into gpio_descs. Those are represented in _CRS as a pathname to a GPIO
device plus the pin's index number: this function is perfect for that
purpose.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/gpio/gpiolib-acpi.c | 3 ++-
include/linux/acpi.h | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 1aacd2a5a1fd..94a3d3d05560 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -111,7 +111,7 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
* controller does not have GPIO chip registered at the moment. This is to
* support probe deferral.
*/
-static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
+struct gpio_desc *acpi_get_gpiod(char *path, int pin)
{
struct gpio_chip *chip;
acpi_handle handle;
@@ -127,6 +127,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
return gpiochip_get_desc(chip, pin);
}
+EXPORT_SYMBOL_GPL(acpi_get_gpiod);
static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
{
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 2f7508c3c2d6..b01109930678 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1080,6 +1080,7 @@ void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const c
bool acpi_gpio_get_irq_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);
+struct gpio_desc *acpi_get_gpiod(char *path, int pin);
#else
static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
struct acpi_resource_gpio **agpio)
@@ -1091,6 +1092,10 @@ static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev,
{
return -ENXIO;
}
+struct gpio_desc *acpi_get_gpiod(char *path, int pin)
+{
+ return NULL;
+}
#endif
static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
--
2.31.1
From cc8092eab5c7dd0ae7ec411ccdd3778e83f749e9 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 12 Dec 2020 23:56:59 +0000
Subject: [PATCH] mfd: 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.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/acpi/pmic/Kconfig | 1 -
drivers/gpio/Kconfig | 1 -
drivers/mfd/Kconfig | 18 --------
drivers/mfd/Makefile | 1 -
drivers/mfd/tps68470.c | 97 ---------------------------------------
5 files changed, 118 deletions(-)
delete mode 100644 drivers/mfd/tps68470.c
diff --git a/drivers/acpi/pmic/Kconfig b/drivers/acpi/pmic/Kconfig
index 56bbcb2ce61b..e27d8ef3a32c 100644
--- a/drivers/acpi/pmic/Kconfig
+++ b/drivers/acpi/pmic/Kconfig
@@ -52,7 +52,6 @@ endif # PMIC_OPREGION
config TPS68470_PMIC_OPREGION
bool "ACPI operation region support for TPS68470 PMIC"
- depends on MFD_TPS68470
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 fa225175e68d..27b43d9c5da6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1347,7 +1347,6 @@ config GPIO_TPS65912
config GPIO_TPS68470
bool "TPS68470 GPIO"
- depends on MFD_TPS68470
help
Select this option to enable GPIO driver for the TPS68470
chip family.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index bdfce7b15621..9a1f648efde0 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1520,24 +1520,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 14fdb188af02..5994e812f479 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.31.1
From 1349b00341f9a4196d975e465046e80dc42566c4 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 15 Jan 2021 12:37:31 +0000
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>
Patchset: cameras
---
MAINTAINERS | 5 +
drivers/platform/x86/Kconfig | 25 +
drivers/platform/x86/Makefile | 5 +
.../platform/x86/intel_skl_int3472_common.c | 100 ++++
.../platform/x86/intel_skl_int3472_common.h | 99 ++++
.../platform/x86/intel_skl_int3472_discrete.c | 489 ++++++++++++++++++
.../platform/x86/intel_skl_int3472_tps68470.c | 145 ++++++
7 files changed, 868 insertions(+)
create mode 100644 drivers/platform/x86/intel_skl_int3472_common.c
create mode 100644 drivers/platform/x86/intel_skl_int3472_common.h
create mode 100644 drivers/platform/x86/intel_skl_int3472_discrete.c
create mode 100644 drivers/platform/x86/intel_skl_int3472_tps68470.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 1bade5b42a40..2aa943def82b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9139,6 +9139,11 @@ S: Maintained
F: arch/x86/include/asm/intel_scu_ipc.h
F: drivers/platform/x86/intel_scu_*
+INTEL SKL INT3472 ACPI DEVICE DRIVER
+M: Daniel Scally <djrscally@gmail.com>
+S: Maintained
+F: drivers/platform/x86/intel_skl_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 ac4125ec0660..ca95ec1cbc4e 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -844,6 +844,31 @@ config INTEL_CHT_INT33FE
device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m
for Type-C device.
+config INTEL_SKL_INT3472
+ tristate "Intel SkyLake ACPI INT3472 Driver"
+ depends on X86 && ACPI
+ select REGMAP_I2C
+ help
+ This driver adds support for the INT3472 ACPI devices found on some
+ Intel SkyLake devices.
+
+ There are 3 kinds of INT3472 ACPI device possible; two for devices
+ designed for Windows (either with or without a physical tps68470
+ PMIC) and one designed for Chrome OS. This driver handles all three
+ situations by discovering information it needs to discern them at
+ runtime.
+
+ If your device was designed for Chrome OS, this driver will provide
+ an ACPI operation region, which must be available before any of the
+ devices using this are probed. For this reason, you should select Y
+ if your device was designed for ChromeOS. This option also configures
+ the designware-i2c driver to be built-in, for the same reason.
+
+ 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"
+
config INTEL_HID_EVENT
tristate "INTEL HID Event"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 581475f59819..3cefe67761af 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -86,6 +86,11 @@ 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_skl_int3472.o
+intel_skl_int3472-objs := intel_skl_int3472_common.o \
+ intel_skl_int3472_discrete.o \
+ intel_skl_int3472_tps68470.o
+
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
# MSI
diff --git a/drivers/platform/x86/intel_skl_int3472_common.c b/drivers/platform/x86/intel_skl_int3472_common.c
new file mode 100644
index 000000000000..08cb9d3c06aa
--- /dev/null
+++ b/drivers/platform/x86/intel_skl_int3472_common.c
@@ -0,0 +1,100 @@
+// 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 "intel_skl_int3472_common.h"
+
+int skl_int3472_get_cldb_buffer(struct acpi_device *adev,
+ struct int3472_cldb *cldb)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_handle handle = adev->handle;
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = 0;
+
+ status = acpi_evaluate_object(handle, "CLDB", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ obj = buffer.pointer;
+ if (!obj) {
+ dev_err(&adev->dev, "ACPI device has no CLDB object\n");
+ return -ENODEV;
+ }
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_err(&adev->dev, "CLDB object is not an ACPI buffer\n");
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ if (obj->buffer.length > sizeof(*cldb)) {
+ dev_err(&adev->dev, "The CLDB buffer is too large\n");
+ ret = -EINVAL;
+ goto out_free_buff;
+ }
+
+ memcpy(cldb, obj->buffer.pointer, obj->buffer.length);
+
+out_free_buff:
+ kfree(buffer.pointer);
+ 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 = 0;
+
+ ret = platform_driver_register(&int3472_discrete);
+ if (ret)
+ return ret;
+
+ ret = i2c_register_driver(THIS_MODULE, &int3472_tps68470);
+ if (ret)
+ goto err_unregister_plat_drv;
+
+ return 0;
+
+err_unregister_plat_drv:
+ 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_skl_int3472_common.h b/drivers/platform/x86/intel_skl_int3472_common.h
new file mode 100644
index 000000000000..4ac6bb2b223f
--- /dev/null
+++ b/drivers/platform/x86/intel_skl_int3472_common.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Author: Dan Scally <djrscally@gmail.com> */
+#include <linux/regulator/machine.h>
+#include <linux/clk-provider.h>
+#include <linux/gpio/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/types.h>
+
+/* PMIC GPIO Types */
+#define INT3472_GPIO_TYPE_RESET 0x00
+#define INT3472_GPIO_TYPE_POWERDOWN 0x01
+#define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c
+#define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b
+#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 27
+#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
+
+#define INT3472_REGULATOR(_NAME, _SUPPLY, _OPS) \
+ (const struct regulator_desc) { \
+ .name = _NAME, \
+ .supply_name = _SUPPLY, \
+ .id = 0, \
+ .type = REGULATOR_VOLTAGE, \
+ .ops = _OPS, \
+ .owner = THIS_MODULE, \
+ }
+
+#define INT3472_GPIO_FUNCTION_REMAP(_PIN, _FUNCTION) \
+ (const struct int3472_gpio_function_remap) { \
+ .documented = _PIN, \
+ .actual = _FUNCTION \
+ }
+
+#define to_int3472_clk(hw) \
+ container_of(hw, struct int3472_gpio_clock, clk_hw)
+
+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_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;
+};
+
+struct int3472_gpio_clock {
+ struct clk *clk;
+ struct clk_hw clk_hw;
+ struct gpio_desc *gpio;
+};
+
+struct int3472_device {
+ struct acpi_device *adev;
+ struct platform_device *pdev;
+ struct acpi_device *sensor;
+ char *sensor_name;
+
+ unsigned int n_gpios; /* how many GPIOs have we seen */
+
+ struct int3472_gpio_regulator regulator;
+ struct int3472_gpio_clock clock;
+
+ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */
+ bool gpios_mapped;
+ struct gpiod_lookup_table gpios;
+};
+
+struct int3472_gpio_function_remap {
+ char *documented;
+ char *actual;
+};
+
+struct int3472_sensor_config {
+ char *sensor_module_name;
+ struct regulator_consumer_supply supply_map;
+ const struct int3472_gpio_function_remap *function_maps;
+};
+
+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);
+int skl_int3472_get_cldb_buffer(struct acpi_device *adev,
+ struct int3472_cldb *cldb);
diff --git a/drivers/platform/x86/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel_skl_int3472_discrete.c
new file mode 100644
index 000000000000..ea7e57f3e3f0
--- /dev/null
+++ b/drivers/platform/x86/intel_skl_int3472_discrete.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+
+#include <linux/acpi.h>
+#include <linux/clkdev.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include "intel_skl_int3472_common.h"
+
+/* 79234640-9e10-4fea-a5c1b5aa8b19756f */
+static const guid_t int3472_gpio_guid =
+ GUID_INIT(0x79234640, 0x9e10, 0x4fea,
+ 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
+
+/* 822ace8f-2814-4174-a56b5f029fe079ee */
+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. 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[] = {
+ INT3472_GPIO_FUNCTION_REMAP("reset", NULL),
+ INT3472_GPIO_FUNCTION_REMAP("powerdown", "reset"),
+ { }
+};
+
+static 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", "i2c-OVTI5648:00"), NULL},
+ /* Surface Go 1&2 - OV5693, Front */
+ { "YHCU", REGULATOR_SUPPLY("avdd", "i2c-INT33BE:00"), NULL},
+};
+
+/*
+ * 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 = { 0 };
+
+static int skl_int3472_clk_enable(struct clk_hw *hw)
+{
+ struct int3472_gpio_clock *clk = to_int3472_clk(hw);
+
+ gpiod_set_value(clk->gpio, 1);
+
+ return 0;
+}
+
+static void skl_int3472_clk_disable(struct clk_hw *hw)
+{
+ struct int3472_gpio_clock *clk = to_int3472_clk(hw);
+
+ gpiod_set_value(clk->gpio, 0);
+}
+
+static int skl_int3472_clk_prepare(struct clk_hw *hw)
+{
+ /*
+ * We're just turning a GPIO on to enable, so nothing to do here, but
+ * we want to provide the op so prepare_enable() works.
+ */
+ return 0;
+}
+
+static void skl_int3472_clk_unprepare(struct clk_hw *hw)
+{
+ /* Likewise, nothing to do here... */
+}
+
+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,
+};
+
+static struct int3472_sensor_config *
+int3472_get_sensor_module_config(struct int3472_device *int3472)
+{
+ unsigned int i = ARRAY_SIZE(int3472_sensor_configs);
+ struct int3472_sensor_config *ret;
+ union acpi_object *obj;
+
+ obj = acpi_evaluate_dsm_typed(int3472->sensor->handle,
+ &cio2_sensor_module_guid, 0x00,
+ 0x01, NULL, ACPI_TYPE_STRING);
+
+ if (!obj) {
+ dev_err(&int3472->pdev->dev,
+ "Failed to get sensor module string from _DSM\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (obj->string.type != ACPI_TYPE_STRING) {
+ dev_err(&int3472->pdev->dev,
+ "Sensor _DSM returned a non-string value\n");
+ ret = ERR_PTR(-EINVAL);
+ goto out_free_obj;
+ }
+
+ ret = ERR_PTR(-ENODEV);
+ while (i--) {
+ if (!strcmp(int3472_sensor_configs[i].sensor_module_name,
+ obj->string.pointer)) {
+ ret = &int3472_sensor_configs[i];
+ goto out_free_obj;
+ }
+ }
+
+out_free_obj:
+ ACPI_FREE(obj);
+ return ret;
+}
+
+static int int3472_map_gpio_to_sensor(struct int3472_device *int3472,
+ struct acpi_resource *ares,
+ char *func, u32 polarity)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ struct int3472_sensor_config *sensor_config;
+ struct gpiod_lookup table_entry;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_status status;
+ int ret;
+
+ sensor_config = int3472_get_sensor_module_config(int3472);
+ if (!IS_ERR(sensor_config) && sensor_config->function_maps) {
+ unsigned int i = 0;
+
+ while (sensor_config->function_maps[i].documented) {
+ if (!strcmp(func, sensor_config->function_maps[i].documented)) {
+ func = sensor_config->function_maps[i].actual;
+
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ if (!func)
+ return 0;
+
+ if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
+ dev_warn(&int3472->pdev->dev, "Too many GPIOs mapped\n");
+ return -EINVAL;
+ }
+
+ 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 = (struct gpiod_lookup)GPIO_LOOKUP_IDX(acpi_dev_name(adev),
+ ares->data.gpio.pin_table[0],
+ func, 0, polarity);
+
+ memcpy(&int3472->gpios.table[int3472->n_sensor_gpios], &table_entry,
+ sizeof(table_entry));
+
+ int3472->n_sensor_gpios++;
+
+ return 0;
+}
+
+static int int3472_register_clock(struct int3472_device *int3472,
+ struct acpi_resource *ares)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ struct clk_init_data init = { };
+ int ret = 0;
+
+ init.name = kasprintf(GFP_KERNEL, "%s-clk", acpi_dev_name(int3472->adev));
+ init.ops = &skl_int3472_clock_ops;
+
+ int3472->clock.gpio = acpi_get_gpiod(path, ares->data.gpio.pin_table[0]);
+ if (IS_ERR(int3472->clock.gpio)) {
+ ret = PTR_ERR(int3472->clock.gpio);
+ goto out_free_init_name;
+ }
+
+ 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 err_put_gpio;
+ }
+
+ ret = clk_register_clkdev(int3472->clock.clk, "xvclk", int3472->sensor_name);
+ if (ret)
+ goto err_unregister_clk;
+
+ goto out_free_init_name;
+
+err_unregister_clk:
+ clk_unregister(int3472->clock.clk);
+err_put_gpio:
+ gpiod_put(int3472->clock.gpio);
+out_free_init_name:
+ kfree(init.name);
+
+ return ret;
+}
+
+static int int3472_register_regulator(struct int3472_device *int3472,
+ struct acpi_resource *ares)
+{
+ char *path = ares->data.gpio.resource_source.string_ptr;
+ struct int3472_sensor_config *sensor_config;
+ struct regulator_init_data init_data = { };
+ struct int3472_gpio_regulator *regulator;
+ struct regulator_config cfg = { };
+ int ret;
+
+ sensor_config = int3472_get_sensor_module_config(int3472);
+ if (IS_ERR_OR_NULL(sensor_config)) {
+ dev_err(&int3472->pdev->dev, "No sensor module config\n");
+ return PTR_ERR(sensor_config);
+ }
+
+ if (!sensor_config->supply_map.supply) {
+ dev_err(&int3472->pdev->dev, "No supply name defined\n");
+ return -ENODEV;
+ }
+
+ init_data.supply_regulator = NULL;
+ init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
+ init_data.num_consumer_supplies = 1;
+ init_data.consumer_supplies = &sensor_config->supply_map;
+
+ snprintf(int3472->regulator.regulator_name, GPIO_REGULATOR_NAME_LENGTH,
+ "int3472-discrete-regulator");
+ 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_gpiod(path, ares->data.gpio.pin_table[0]);
+ if (IS_ERR(int3472->regulator.gpio)) {
+ ret = PTR_ERR(int3472->regulator.gpio);
+ goto err_free_regulator;
+ }
+
+ 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(regulator->gpio);
+err_free_regulator:
+ kfree(regulator);
+
+ return ret;
+}
+
+/**
+ * int3472_handle_gpio_resources: maps PMIC resources to consuming sensor
+ * @ares: A pointer to a &struct acpi_resource
+ * @data: A pointer to a &struct int3472_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. 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:
+ * * 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 int3472_handle_gpio_resources(struct acpi_resource *ares,
+ void *data)
+{
+ struct int3472_device *int3472 = data;
+ union acpi_object *obj;
+ int ret = 0;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO ||
+ ares->data.gpio.connection_type != ACPI_RESOURCE_GPIO_TYPE_IO)
+ return EINVAL; /* Deliberately positive so parsing continues */
+
+ /*
+ * n_gpios + 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->n_gpios + 2,
+ NULL, ACPI_TYPE_INTEGER);
+
+ if (!obj) {
+ dev_warn(&int3472->pdev->dev,
+ "No _DSM entry for this GPIO pin\n");
+ return ENODEV;
+ }
+
+ switch (obj->integer.value & 0xff) {
+ case INT3472_GPIO_TYPE_RESET:
+ ret = int3472_map_gpio_to_sensor(int3472, ares, "reset",
+ GPIO_ACTIVE_LOW);
+ if (ret)
+ dev_err(&int3472->pdev->dev,
+ "Failed to map reset pin to sensor\n");
+
+ break;
+ case INT3472_GPIO_TYPE_POWERDOWN:
+ ret = int3472_map_gpio_to_sensor(int3472, ares, "powerdown",
+ GPIO_ACTIVE_LOW);
+ if (ret)
+ dev_err(&int3472->pdev->dev,
+ "Failed to map powerdown pin to sensor\n");
+
+ break;
+ case INT3472_GPIO_TYPE_CLK_ENABLE:
+ ret = int3472_register_clock(int3472, ares);
+ if (ret)
+ dev_err(&int3472->pdev->dev,
+ "Failed to map clock to sensor\n");
+
+ break;
+ case INT3472_GPIO_TYPE_POWER_ENABLE:
+ ret = int3472_register_regulator(int3472, ares);
+ if (ret) {
+ dev_err(&int3472->pdev->dev,
+ "Failed to map regulator to sensor\n");
+ }
+
+ break;
+ case INT3472_GPIO_TYPE_PRIVACY_LED:
+ ret = int3472_map_gpio_to_sensor(int3472, ares, "indicator-led",
+ GPIO_ACTIVE_HIGH);
+ if (ret)
+ dev_err(&int3472->pdev->dev,
+ "Failed to map indicator led to sensor\n");
+
+ break;
+ default:
+ dev_warn(&int3472->pdev->dev,
+ "GPIO type 0x%llx unknown; the sensor may not work\n",
+ (obj->integer.value & 0xff));
+ ret = EINVAL;
+ }
+
+ int3472->n_gpios++;
+ ACPI_FREE(obj);
+
+ return ret;
+}
+
+static int int3472_parse_crs(struct int3472_device *int3472)
+{
+ struct list_head resource_list;
+ int ret = 0;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ ret = acpi_dev_get_resources(int3472->adev, &resource_list,
+ int3472_handle_gpio_resources, int3472);
+
+ if (!ret) {
+ gpiod_add_lookup_table(&int3472->gpios);
+ int3472->gpios_mapped = true;
+ }
+
+ 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_device *int3472;
+ struct int3472_cldb cldb;
+ int ret = 0;
+
+ ret = skl_int3472_get_cldb_buffer(adev, &cldb);
+ if (ret || cldb.control_logic_type != 1)
+ return -EINVAL;
+
+ int3472 = kzalloc(sizeof(*int3472) +
+ ((INT3472_MAX_SENSOR_GPIOS + 1) * sizeof(struct gpiod_lookup)),
+ GFP_KERNEL);
+ if (!int3472)
+ return -ENOMEM;
+
+ int3472->adev = adev;
+ int3472->pdev = pdev;
+ platform_set_drvdata(pdev, int3472);
+
+ int3472->sensor = acpi_dev_get_next_dep_dev(adev, NULL);
+ if (!int3472->sensor) {
+ dev_err(&pdev->dev,
+ "This INT3472 entry seems to have no dependents.\n");
+ ret = -ENODEV;
+ goto err_free_int3472;
+ }
+ int3472->sensor_name = i2c_acpi_dev_name(int3472->sensor);
+ int3472->gpios.dev_id = int3472->sensor_name;
+
+ ret = int3472_parse_crs(int3472);
+ if (ret) {
+ skl_int3472_discrete_remove(pdev);
+ goto err_return_ret;
+ }
+
+ return 0;
+
+err_free_int3472:
+ kfree(int3472);
+err_return_ret:
+ return ret;
+}
+
+int skl_int3472_discrete_remove(struct platform_device *pdev)
+{
+ struct int3472_device *int3472;
+
+ int3472 = platform_get_drvdata(pdev);
+
+ if (int3472->gpios_mapped)
+ gpiod_remove_lookup_table(&int3472->gpios);
+
+ if (!IS_ERR_OR_NULL(int3472->regulator.rdev)) {
+ gpiod_put(int3472->regulator.gpio);
+ regulator_unregister(int3472->regulator.rdev);
+ }
+
+ if (!IS_ERR_OR_NULL(int3472->clock.clk)) {
+ gpiod_put(int3472->clock.gpio);
+ clk_unregister(int3472->clock.clk);
+ }
+
+ acpi_dev_put(int3472->sensor);
+
+ kfree(int3472->sensor_name);
+ kfree(int3472);
+
+ return 0;
+}
diff --git a/drivers/platform/x86/intel_skl_int3472_tps68470.c b/drivers/platform/x86/intel_skl_int3472_tps68470.c
new file mode 100644
index 000000000000..3fe27ec0caff
--- /dev/null
+++ b/drivers/platform/x86/intel_skl_int3472_tps68470.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dan Scally <djrscally@gmail.com> */
+
+#include <linux/i2c.h>
+#include <linux/mfd/tps68470.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "intel_skl_int3472_common.h"
+
+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 struct platform_device *
+skl_int3472_register_pdev(const char *name, struct device *parent)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
+ if (IS_ERR_OR_NULL(pdev))
+ return ERR_PTR(-ENOMEM);
+
+ pdev->dev.parent = parent;
+ pdev->driver_override = kstrndup(pdev->name, INT3472_PDEV_MAX_NAME_LEN,
+ GFP_KERNEL);
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ platform_device_put(pdev);
+ return ERR_PTR(ret);
+ }
+
+ return pdev;
+}
+
+int skl_int3472_tps68470_probe(struct i2c_client *client)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&client->dev);
+ struct platform_device *regulator_dev;
+ struct platform_device *opregion_dev;
+ struct platform_device *gpio_dev;
+ struct int3472_cldb cldb = { 0 };
+ struct platform_device *clk_dev;
+ bool cldb_present = true;
+ struct regmap *regmap;
+ int ret = 0;
+
+ regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "devm_regmap_init_i2c Error %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;
+ }
+
+ /*
+ * 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; crash and burn.
+ */
+ ret = skl_int3472_get_cldb_buffer(adev, &cldb);
+ if (!ret && cldb.control_logic_type != 2)
+ return -EINVAL;
+
+ if (ret)
+ cldb_present = false;
+
+ gpio_dev = skl_int3472_register_pdev("tps68470-gpio", &client->dev);
+ if (IS_ERR(gpio_dev))
+ return PTR_ERR(gpio_dev);
+
+ if (cldb_present) {
+ clk_dev = skl_int3472_register_pdev("tps68470-clk",
+ &client->dev);
+ if (IS_ERR(clk_dev)) {
+ ret = PTR_ERR(clk_dev);
+ goto err_free_gpio;
+ }
+
+ regulator_dev = skl_int3472_register_pdev("tps68470-regulator",
+ &client->dev);
+ if (IS_ERR(regulator_dev)) {
+ ret = PTR_ERR(regulator_dev);
+ goto err_free_clk;
+ }
+ } else {
+ opregion_dev = skl_int3472_register_pdev("tps68470_pmic_opregion",
+ &client->dev);
+ if (IS_ERR(opregion_dev)) {
+ ret = PTR_ERR(opregion_dev);
+ goto err_free_gpio;
+ }
+ }
+
+ return 0;
+
+err_free_clk:
+ platform_device_put(clk_dev);
+err_free_gpio:
+ platform_device_put(gpio_dev);
+
+ return ret;
+}
--
2.31.1
From 79abed3ac8ba43d256cdcc026ac5614563affeb2 Mon Sep 17 00:00:00 2001
From: Jake Day <jake@ninebysix.com>
Date: Fri, 25 Sep 2020 10:24:53 -0400
Subject: [PATCH] media: i2c: Add support for the OV5693 image sensor
The OV5693 is a 5 Mpx CMOS image sensor, connected via MIPI CSI-2
in a one or two lane configuration.
Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@gmail.com>
Patchset: cameras
---
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ad5823.h | 63 ++
drivers/media/i2c/ov5693.c | 1788 ++++++++++++++++++++++++++++++++++++
drivers/media/i2c/ov5693.h | 1430 ++++++++++++++++++++++++++++
5 files changed, 3293 insertions(+)
create mode 100644 drivers/media/i2c/ad5823.h
create mode 100644 drivers/media/i2c/ov5693.c
create mode 100644 drivers/media/i2c/ov5693.h
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 6eed3209ee2d..fc96e3d4764b 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -972,6 +972,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 a3149dce21bb..cac649668a4e 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.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/ad5823.h b/drivers/media/i2c/ad5823.h
new file mode 100644
index 000000000000..f1362cd69f6e
--- /dev/null
+++ b/drivers/media/i2c/ad5823.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Support for AD5823 VCM.
+ *
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#ifndef __AD5823_H__
+#define __AD5823_H__
+
+#include <linux/types.h>
+
+#define AD5823_VCM_ADDR 0x0c
+
+#define AD5823_REG_RESET 0x01
+#define AD5823_REG_MODE 0x02
+#define AD5823_REG_VCM_MOVE_TIME 0x03
+#define AD5823_REG_VCM_CODE_MSB 0x04
+#define AD5823_REG_VCM_CODE_LSB 0x05
+#define AD5823_REG_VCM_THRESHOLD_MSB 0x06
+#define AD5823_REG_VCM_THRESHOLD_LSB 0x07
+
+#define AD5823_REG_LENGTH 0x1
+
+#define AD5823_RING_CTRL_ENABLE 0x04
+#define AD5823_RING_CTRL_DISABLE 0x00
+
+#define AD5823_RESONANCE_PERIOD 100000
+#define AD5823_RESONANCE_COEF 512
+#define AD5823_HIGH_FREQ_RANGE 0x80
+
+#define VCM_CODE_MSB_MASK 0xfc
+#define AD5823_INIT_FOCUS_POS 350
+
+enum ad5823_tok_type {
+ AD5823_8BIT = 0x1,
+ AD5823_16BIT = 0x2,
+};
+
+enum ad5823_vcm_mode {
+ AD5823_ARC_RES0 = 0x0, /* Actuator response control RES1 */
+ AD5823_ARC_RES1 = 0x1, /* Actuator response control RES0.5 */
+ AD5823_ARC_RES2 = 0x2, /* Actuator response control RES2 */
+ AD5823_ESRC = 0x3, /* Enhanced slew rate control */
+ AD5823_DIRECT = 0x4, /* Direct control */
+};
+
+#define AD5823_INVALID_CONFIG 0xffffffff
+#define AD5823_MAX_FOCUS_POS 1023
+#define DELAY_PER_STEP_NS 1000000
+#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
+#endif
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
new file mode 100644
index 000000000000..32485e4ed42b
--- /dev/null
+++ b/drivers/media/i2c/ov5693.c
@@ -0,0 +1,1788 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for OmniVision OV5693 1080p HD camera sensor.
+ *
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/moduleparam.h>
+#include <media/v4l2-device.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/regulator/consumer.h>
+
+#include "ov5693.h"
+#include "ad5823.h"
+
+#define __cci_delay(t) \
+ do { \
+ if ((t) < 10) { \
+ usleep_range((t) * 1000, ((t) + 1) * 1000); \
+ } else { \
+ msleep((t)); \
+ } \
+ } while (0)
+
+/* Value 30ms reached through experimentation on byt ecs.
+ * The DS specifies a much lower value but when using a smaller value
+ * the I2C bus sometimes locks up permanently when starting the camera.
+ * This issue could not be reproduced on cht, so we can reduce the
+ * delay value to a lower value when insmod.
+ */
+static uint up_delay = 30;
+module_param(up_delay, uint, 0644);
+MODULE_PARM_DESC(up_delay,
+ "Delay prior to the first CCI transaction for ov5693");
+
+
+/* Exposure/gain */
+
+#define OV5693_EXPOSURE_CTRL_HH_REG 0x3500
+#define OV5693_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(18, 16)) >> 16)
+#define OV5693_EXPOSURE_CTRL_H_REG 0x3501
+#define OV5693_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_EXPOSURE_CTRL_L_REG 0x3502
+#define OV5693_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0))
+#define OV5693_EXPOSURE_GAIN_MANUAL_REG 0x3509
+
+#define OV5693_GAIN_CTRL_H_REG 0x3504
+#define OV5693_GAIN_CTRL_H(v) (((v) & GENMASK(9, 8)) >> 8)
+#define OV5693_GAIN_CTRL_L_REG 0x3505
+#define OV5693_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0))
+
+#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_FORMAT2_REG 0x3821
+#define OV5693_FORMAT2_HSYNC_EN BIT(6)
+#define OV5693_FORMAT2_FST_VBIN_EN BIT(5)
+#define OV5693_FORMAT2_FST_HBIN_EN BIT(4)
+#define OV5693_FORMAT2_ISP_HORZ_VAR2_EN BIT(3)
+#define OV5693_FORMAT2_FLIP_HORZ_ISP_EN BIT(2)
+#define OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1)
+#define OV5693_FORMAT2_SYNC_HBIN_EN BIT(0)
+
+/* ISP */
+
+#define OV5693_ISP_CTRL0_REG 0x5000
+#define OV5693_ISP_CTRL0_LENC_EN BIT(7)
+#define OV5693_ISP_CTRL0_WHITE_BALANCE_EN BIT(4)
+#define OV5693_ISP_CTRL0_DPC_BLACK_EN BIT(2)
+#define OV5693_ISP_CTRL0_DPC_WHITE_EN BIT(1)
+#define OV5693_ISP_CTRL1_REG 0x5001
+#define OV5693_ISP_CTRL1_BLC_EN BIT(0)
+
+/* native and active pixel array size. */
+#define OV5693_NATIVE_WIDTH 2688U
+#define OV5693_NATIVE_HEIGHT 1984U
+#define OV5693_PIXEL_ARRAY_LEFT 48U
+#define OV5693_PIXEL_ARRAY_TOP 20U
+#define OV5693_PIXEL_ARRAY_WIDTH 2592U
+#define OV5693_PIXEL_ARRAY_HEIGHT 1944U
+
+#define OV5693_PPL_DEFAULT 2800
+
+static int vcm_ad_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ struct i2c_msg msg;
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+
+ msg.addr = VCM_ADDR;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &buf[0];
+
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err != 1) {
+ dev_err(&client->dev, "%s: vcm i2c fail, err code = %d\n",
+ __func__, err);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ struct i2c_msg msg;
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ msg.addr = AD5823_VCM_ADDR;
+ msg.flags = 0;
+ msg.len = 0x02;
+ msg.buf = &buf[0];
+
+ if (i2c_transfer(client->adapter, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = 0;
+
+ msg[0].addr = AD5823_VCM_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = 0x01;
+ msg[0].buf = &buf[0];
+
+ msg[1].addr = 0x0c;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 0x01;
+ msg[1].buf = &buf[1];
+ *val = 0;
+ if (i2c_transfer(client->adapter, msg, 2) != 2)
+ return -EIO;
+ *val = buf[1];
+ return 0;
+}
+
+static const u32 ov5693_embedded_effective_size = 28;
+
+/* i2c read/write stuff */
+static int ov5693_read_reg(struct i2c_client *client,
+ u16 data_length, u16 reg, u16 *val)
+{
+ int err;
+ struct i2c_msg msg[2];
+ unsigned char data[6];
+
+ if (!client->adapter) {
+ dev_err(&client->dev, "%s error, no client->adapter\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT
+ && data_length != OV5693_32BIT) {
+ dev_err(&client->dev, "%s error, invalid data length\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(msg, 0, sizeof(msg));
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = I2C_MSG_LENGTH;
+ msg[0].buf = data;
+
+ /* high byte goes out first */
+ data[0] = (u8)(reg >> 8);
+ data[1] = (u8)(reg & 0xff);
+
+ msg[1].addr = client->addr;
+ msg[1].len = data_length;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = data;
+
+ err = i2c_transfer(client->adapter, msg, 2);
+ if (err != 2) {
+ if (err >= 0)
+ err = -EIO;
+ dev_err(&client->dev,
+ "read from offset 0x%x error %d", reg, err);
+ return err;
+ }
+
+ *val = 0;
+ /* high byte comes first */
+ if (data_length == OV5693_8BIT)
+ *val = (u8)data[0];
+ else if (data_length == OV5693_16BIT)
+ *val = be16_to_cpu(*(__be16 *)&data[0]);
+ else
+ *val = be32_to_cpu(*(__be32 *)&data[0]);
+
+ return 0;
+}
+
+static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data)
+{
+ struct i2c_msg msg;
+ const int num_msg = 1;
+ int ret;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = data;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret == num_msg ? 0 : -EIO;
+}
+
+static int vcm_dw_i2c_write(struct i2c_client *client, u16 data)
+{
+ struct i2c_msg msg;
+ const int num_msg = 1;
+ int ret;
+ __be16 val;
+
+ val = cpu_to_be16(data);
+ msg.addr = VCM_ADDR;
+ msg.flags = 0;
+ msg.len = OV5693_16BIT;
+ msg.buf = (void *)&val;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret == num_msg ? 0 : -EIO;
+}
+
+/*
+ * Theory: per datasheet, the two VCMs both allow for a 2-byte read.
+ * The DW9714 doesn't actually specify what this does (it has a
+ * two-byte write-only protocol, but specifies the read sequence as
+ * legal), but it returns the same data (zeroes) always, after an
+ * undocumented initial NAK. The AD5823 has a one-byte address
+ * register to which all writes go, and subsequent reads will cycle
+ * through the 8 bytes of registers. Notably, the default values (the
+ * device is always power-cycled affirmatively, so we can rely on
+ * these) in AD5823 are not pairwise repetitions of the same 16 bit
+ * word. So all we have to do is sequentially read two bytes at a
+ * time and see if we detect a difference in any of the first four
+ * pairs.
+ */
+static int vcm_detect(struct i2c_client *client)
+{
+ int i, ret;
+ struct i2c_msg msg;
+ u16 data0 = 0, data;
+
+ for (i = 0; i < 4; i++) {
+ msg.addr = VCM_ADDR;
+ msg.flags = I2C_M_RD;
+ msg.len = sizeof(data);
+ msg.buf = (u8 *)&data;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ /*
+ * DW9714 always fails the first read and returns
+ * zeroes for subsequent ones
+ */
+ if (i == 0 && ret == -EREMOTEIO) {
+ data0 = 0;
+ continue;
+ }
+
+ if (i == 0)
+ data0 = data;
+
+ if (data != data0)
+ return VCM_AD5823;
+ }
+ return ret == 1 ? VCM_DW9714 : ret;
+}
+
+static int ov5693_write_reg(struct i2c_client *client, u16 data_length,
+ u16 reg, u16 val)
+{
+ int ret;
+ unsigned char data[4] = {0};
+ __be16 *wreg = (void *)data;
+ const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
+
+ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) {
+ dev_err(&client->dev,
+ "%s error, invalid data_length\n", __func__);
+ return -EINVAL;
+ }
+
+ /* high byte goes out first */
+ *wreg = cpu_to_be16(reg);
+
+ if (data_length == OV5693_8BIT) {
+ data[2] = (u8)(val);
+ } else {
+ /* OV5693_16BIT */
+ __be16 *wdata = (void *)&data[2];
+
+ *wdata = cpu_to_be16(val);
+ }
+
+ ret = ov5693_i2c_write(client, len, data);
+ if (ret)
+ dev_err(&client->dev,
+ "write error: wrote 0x%x to offset 0x%x error %d",
+ val, reg, ret);
+
+ return ret;
+}
+
+/*
+ * ov5693_write_reg_array - Initializes a list of OV5693 registers
+ * @client: i2c driver client structure
+ * @reglist: list of registers to be written
+ *
+ * This function initializes a list of registers. When consecutive addresses
+ * are found in a row on the list, this function creates a buffer and sends
+ * consecutive data in a single i2c_transfer().
+ *
+ * __ov5693_flush_reg_array, __ov5693_buf_reg_array() and
+ * __ov5693_write_reg_is_consecutive() are internal functions to
+ * ov5693_write_reg_array_fast() and should be not used anywhere else.
+ *
+ */
+
+static int __ov5693_flush_reg_array(struct i2c_client *client,
+ struct ov5693_write_ctrl *ctrl)
+{
+ u16 size;
+ __be16 *reg = (void *)&ctrl->buffer.addr;
+
+ if (ctrl->index == 0)
+ return 0;
+
+ size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
+
+ *reg = cpu_to_be16(ctrl->buffer.addr);
+ ctrl->index = 0;
+
+ return ov5693_i2c_write(client, size, (u8 *)reg);
+}
+
+static int __ov5693_buf_reg_array(struct i2c_client *client,
+ struct ov5693_write_ctrl *ctrl,
+ const struct ov5693_reg *next)
+{
+ int size;
+ __be16 *data16;
+
+ switch (next->type) {
+ case OV5693_8BIT:
+ size = 1;
+ ctrl->buffer.data[ctrl->index] = (u8)next->val;
+ break;
+ case OV5693_16BIT:
+ size = 2;
+
+ data16 = (void *)&ctrl->buffer.data[ctrl->index];
+ *data16 = cpu_to_be16((u16)next->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* When first item is added, we need to store its starting address */
+ if (ctrl->index == 0)
+ ctrl->buffer.addr = next->reg;
+
+ ctrl->index += size;
+
+ /*
+ * Buffer cannot guarantee free space for u32? Better flush it to avoid
+ * possible lack of memory for next item.
+ */
+ if (ctrl->index + sizeof(u16) >= OV5693_MAX_WRITE_BUF_SIZE)
+ return __ov5693_flush_reg_array(client, ctrl);
+
+ return 0;
+}
+
+static int __ov5693_write_reg_is_consecutive(struct i2c_client *client,
+ struct ov5693_write_ctrl *ctrl,
+ const struct ov5693_reg *next)
+{
+ if (ctrl->index == 0)
+ return 1;
+
+ return ctrl->buffer.addr + ctrl->index == next->reg;
+}
+
+static int ov5693_write_reg_array(struct i2c_client *client,
+ const struct ov5693_reg *reglist)
+{
+ const struct ov5693_reg *next = reglist;
+ struct ov5693_write_ctrl ctrl;
+ int err;
+
+ ctrl.index = 0;
+ for (; next->type != OV5693_TOK_TERM; next++) {
+ switch (next->type & OV5693_TOK_MASK) {
+ case OV5693_TOK_DELAY:
+ err = __ov5693_flush_reg_array(client, &ctrl);
+ if (err)
+ return err;
+ msleep(next->val);
+ break;
+ default:
+ /*
+ * If next address is not consecutive, data needs to be
+ * flushed before proceed.
+ */
+ if (!__ov5693_write_reg_is_consecutive(client, &ctrl,
+ next)) {
+ err = __ov5693_flush_reg_array(client, &ctrl);
+ if (err)
+ return err;
+ }
+ err = __ov5693_buf_reg_array(client, &ctrl, next);
+ if (err) {
+ dev_err(&client->dev,
+ "%s: write error, aborted\n",
+ __func__);
+ return err;
+ }
+ break;
+ }
+ }
+
+ return __ov5693_flush_reg_array(client, &ctrl);
+}
+
+static int ov5693_read_otp_reg_array(struct i2c_client *client, u16 size,
+ u16 addr, u8 *buf)
+{
+ u16 index;
+ int ret;
+ u16 *pVal = NULL;
+
+ for (index = 0; index <= size; index++) {
+ pVal = (u16 *)(buf + index);
+ ret =
+ ov5693_read_reg(client, OV5693_8BIT, addr + index,
+ pVal);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ int ret;
+ int i;
+ u8 *b = buf;
+
+ dev->otp_size = 0;
+ for (i = 1; i < OV5693_OTP_BANK_MAX; i++) {
+ /*set bank NO and OTP read mode. */
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_OTP_BANK_REG,
+ (i | 0xc0)); //[7:6] 2'b11 [5:0] bank no
+ if (ret) {
+ dev_err(&client->dev, "failed to prepare OTP page\n");
+ return ret;
+ }
+ //dev_dbg(&client->dev, "write 0x%x->0x%x\n",OV5693_OTP_BANK_REG,(i|0xc0));
+
+ /*enable read */
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_OTP_READ_REG,
+ OV5693_OTP_MODE_READ); // enable :1
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to set OTP reading mode page");
+ return ret;
+ }
+ //dev_dbg(&client->dev, "write 0x%x->0x%x\n",
+ // OV5693_OTP_READ_REG,OV5693_OTP_MODE_READ);
+
+ /* Reading the OTP data array */
+ ret = ov5693_read_otp_reg_array(client, OV5693_OTP_BANK_SIZE,
+ OV5693_OTP_START_ADDR,
+ b);
+ if (ret) {
+ dev_err(&client->dev, "failed to read OTP data\n");
+ return ret;
+ }
+
+ //dev_dbg(&client->dev,
+ // "BANK[%2d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ // i, *b, *(b+1), *(b+2), *(b+3), *(b+4), *(b+5), *(b+6), *(b+7),
+ // *(b+8), *(b+9), *(b+10), *(b+11), *(b+12), *(b+13), *(b+14), *(b+15));
+
+ //Intel OTP map, try to read 320byts first.
+ if (i == 21) {
+ if ((*b) == 0) {
+ dev->otp_size = 320;
+ break;
+ }
+ /* (*b) != 0 */
+ b = buf;
+ continue;
+ } else if (i ==
+ 24) { //if the first 320bytes data doesn't not exist, try to read the next 32bytes data.
+ if ((*b) == 0) {
+ dev->otp_size = 32;
+ break;
+ }
+ /* (*b) != 0 */
+ b = buf;
+ continue;
+ } else if (i ==
+ 27) { //if the prvious 32bytes data doesn't exist, try to read the next 32bytes data again.
+ if ((*b) == 0) {
+ dev->otp_size = 32;
+ break;
+ }
+ /* (*b) != 0 */
+ dev->otp_size = 0; // no OTP data.
+ break;
+ }
+
+ b = b + OV5693_OTP_BANK_SIZE;
+ }
+ return 0;
+}
+
+/*
+ * Read otp data and store it into a kmalloced buffer.
+ * The caller must kfree the buffer when no more needed.
+ * @size: set to the size of the returned otp data.
+ */
+static void *ov5693_otp_read(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 *buf;
+ int ret;
+
+ buf = devm_kzalloc(&client->dev, (OV5693_OTP_DATA_SIZE + 16), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ //otp valid after mipi on and sw stream on
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_FRAME_OFF_NUM, 0x00);
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_SW_STREAM, OV5693_START_STREAMING);
+
+ ret = __ov5693_otp_read(sd, buf);
+
+ //mipi off and sw stream off after otp read
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_FRAME_OFF_NUM, 0x0f);
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_SW_STREAM, OV5693_STOP_STREAMING);
+
+ /* Driver has failed to find valid data */
+ if (ret) {
+ dev_err(&client->dev, "sensor found no valid OTP data\n");
+ return ERR_PTR(ret);
+ }
+
+ return buf;
+}
+
+static int ov5693_update_bits(struct ov5693_device *sensor, u16 address,
+ u16 mask, u16 bits)
+{
+ u16 value = 0;
+ int ret;
+
+ ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT, address, &value);
+ if (ret)
+ return ret;
+
+ value &= ~mask;
+ value |= bits;
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT, address, value);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Flip */
+
+static int ov5693_flip_vert_configure(struct ov5693_device *sensor, bool enable)
+{
+ u8 bits = OV5693_FORMAT1_FLIP_VERT_ISP_EN |
+ OV5693_FORMAT1_FLIP_VERT_SENSOR_EN;
+ int ret;
+
+ ret = ov5693_update_bits(sensor, OV5693_FORMAT1_REG, bits,
+ enable ? bits : 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ov5693_flip_horz_configure(struct ov5693_device *sensor, bool enable)
+{
+ u8 bits = OV5693_FORMAT2_FLIP_HORZ_ISP_EN |
+ OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN;
+ int ret;
+
+ ret = ov5693_update_bits(sensor, OV5693_FORMAT2_REG, bits,
+ enable ? bits : 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * This returns the exposure time being used. This should only be used
+ * for filling in EXIF data, not for actual image processing.
+ */
+static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 reg_v, reg_v2;
+ int ret;
+
+ /* get exposure */
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_L,
+ &reg_v);
+ if (ret)
+ goto err;
+
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_M,
+ &reg_v2);
+ if (ret)
+ goto err;
+
+ reg_v += reg_v2 << 8;
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_EXPOSURE_H,
+ &reg_v2);
+ if (ret)
+ goto err;
+
+ *value = reg_v + (((u32)reg_v2 << 16));
+err:
+ return ret;
+}
+
+static int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = -EINVAL;
+ u8 vcm_code;
+
+ ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code);
+ if (ret)
+ return ret;
+
+ /* set reg VCM_CODE_MSB Bit[1:0] */
+ vcm_code = (vcm_code & VCM_CODE_MSB_MASK) |
+ ((val >> 8) & ~VCM_CODE_MSB_MASK);
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code);
+ if (ret)
+ return ret;
+
+ /* set reg VCM_CODE_LSB Bit[7:0] */
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, (val & 0xff));
+ if (ret)
+ return ret;
+
+ /* set required vcm move time */
+ vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF
+ - AD5823_HIGH_FREQ_RANGE;
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code);
+
+ return ret;
+}
+
+static int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value)
+{
+ value = min(value, AD5823_MAX_FOCUS_POS);
+ return ad5823_t_focus_vcm(sd, value);
+}
+
+static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ dev_dbg(&client->dev, "%s: FOCUS_POS: 0x%x\n", __func__, value);
+ value = clamp(value, 0, OV5693_VCM_MAX_FOCUS_POS);
+ if (dev->vcm == VCM_DW9714) {
+ if (dev->vcm_update) {
+ ret = vcm_dw_i2c_write(client, VCM_PROTECTION_OFF);
+ if (ret)
+ return ret;
+ ret = vcm_dw_i2c_write(client, DIRECT_VCM);
+ if (ret)
+ return ret;
+ ret = vcm_dw_i2c_write(client, VCM_PROTECTION_ON);
+ if (ret)
+ return ret;
+ dev->vcm_update = false;
+ }
+ ret = vcm_dw_i2c_write(client,
+ vcm_val(value, VCM_DEFAULT_S));
+ } else if (dev->vcm == VCM_AD5823) {
+ ad5823_t_focus_abs(sd, value);
+ }
+ if (ret == 0) {
+ dev->number_of_steps = value - dev->focus;
+ dev->focus = value;
+ dev->timestamp_t_focus_abs = ktime_get();
+ } else
+ dev_err(&client->dev,
+ "%s: i2c failed. ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ return ov5693_t_focus_abs(sd, dev->focus + value);
+}
+
+#define DELAY_PER_STEP_NS 1000000
+#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
+
+/* Exposure */
+
+static int ov5693_get_exposure(struct ov5693_device *sensor)
+{
+ u16 reg_v, reg_v2;
+ int ret = 0;
+
+ /* get exposure */
+ ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_EXPOSURE_L,
+ &reg_v);
+ if (ret)
+ return ret;
+
+ ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_EXPOSURE_M,
+ &reg_v2);
+ if (ret)
+ return ret;
+
+ reg_v += reg_v2 << 8;
+ ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_EXPOSURE_H,
+ &reg_v2);
+ if (ret)
+ return ret;
+
+ printk("exposure set to: %u\n", reg_v + (((u32)reg_v2 << 16)));
+ return ret;
+}
+
+static int ov5693_exposure_configure(struct ov5693_device *sensor, u32 exposure)
+{
+ int ret;
+
+ ov5693_get_exposure(sensor);
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_EXPOSURE_CTRL_HH_REG, OV5693_EXPOSURE_CTRL_HH(exposure));
+ if (ret)
+ return ret;
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_EXPOSURE_CTRL_H_REG, OV5693_EXPOSURE_CTRL_H(exposure));
+ if (ret)
+ return ret;
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_EXPOSURE_CTRL_L_REG, OV5693_EXPOSURE_CTRL_L(exposure));
+ if (ret)
+ return ret;
+ ov5693_get_exposure(sensor);
+
+ return 0;
+}
+
+/* Gain */
+
+static int ov5693_get_gain(struct ov5693_device *sensor, u32 *gain)
+{
+ u16 gain_l, gain_h;
+ int ret = 0;
+
+ ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_GAIN_CTRL_L_REG,
+ &gain_l);
+ if (ret)
+ return ret;
+
+ ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_GAIN_CTRL_H_REG,
+ &gain_h);
+ if (ret)
+ return ret;
+
+ *gain = (u32)(((gain_h >> 8) & 0x03) |
+ (gain_l & 0xff));
+
+ return ret;
+}
+static int ov5693_gain_configure(struct ov5693_device *sensor, u32 gain)
+{
+ int ret;
+
+ /* A 1.0 gain is 0x400 */
+ gain = (gain * 1024)/1000;
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_16BIT,
+ OV5693_MWB_RED_GAIN_H, gain);
+ if (ret) {
+ dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_MWB_RED_GAIN_H);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_16BIT,
+ OV5693_MWB_GREEN_GAIN_H, gain);
+ if (ret) {
+ dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_MWB_RED_GAIN_H);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_16BIT,
+ OV5693_MWB_BLUE_GAIN_H, gain);
+ if (ret) {
+ dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_MWB_RED_GAIN_H);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov5693_analog_gain_configure(struct ov5693_device *sensor, u32 gain)
+{
+ int ret;
+
+ /* Analog gain */
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_AGC_L, gain & 0xff);
+ if (ret) {
+ dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_AGC_L);
+ return ret;
+ }
+
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_AGC_H, (gain >> 8) & 0xff);
+ if (ret) {
+ dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ __func__, OV5693_AGC_H);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5693_device *dev =
+ container_of(ctrl->handler, struct ov5693_device, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FOCUS_ABSOLUTE:
+ dev_dbg(&client->dev, "%s: CID_FOCUS_ABSOLUTE:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_t_focus_abs(&dev->sd, ctrl->val);
+ break;
+ case V4L2_CID_FOCUS_RELATIVE:
+ dev_dbg(&client->dev, "%s: CID_FOCUS_RELATIVE:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_t_focus_rel(&dev->sd, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&client->dev, "%s: CID_EXPOSURE:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_exposure_configure(dev, ctrl->val);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ dev_dbg(&client->dev, "%s: CID_ANALOGUE_GAIN:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_analog_gain_configure(dev, ctrl->val);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ dev_dbg(&client->dev, "%s: CID_DIGITAL_GAIN:%d.\n",
+ __func__, ctrl->val);
+ ret = ov5693_gain_configure(dev, ctrl->val);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_HFLIP:
+ return ov5693_flip_horz_configure(dev, !!ctrl->val);
+ case V4L2_CID_VFLIP:
+ return ov5693_flip_vert_configure(dev, !!ctrl->val);
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int ov5693_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5693_device *dev =
+ container_of(ctrl->handler, struct ov5693_device, ctrl_handler);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_ABSOLUTE:
+ ret = ov5693_q_exposure(&dev->sd, &ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ ret = ov5693_get_gain(dev, &ctrl->val);
+ break;
+ case V4L2_CID_FOCUS_ABSOLUTE:
+ /* NOTE: there was atomisp-specific function ov5693_q_focus_abs() */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5693_ctrl_ops = {
+ .s_ctrl = ov5693_s_ctrl,
+ .g_volatile_ctrl = ov5693_g_volatile_ctrl
+};
+
+static const struct v4l2_ctrl_config ov5693_controls[] = {
+ {
+ .ops = &ov5693_ctrl_ops,
+ .id = V4L2_CID_FOCUS_ABSOLUTE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "focus move absolute",
+ .min = 0,
+ .max = OV5693_VCM_MAX_FOCUS_POS,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ },
+ {
+ .ops = &ov5693_ctrl_ops,
+ .id = V4L2_CID_FOCUS_RELATIVE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "focus move relative",
+ .min = OV5693_VCM_MAX_FOCUS_NEG,
+ .max = OV5693_VCM_MAX_FOCUS_POS,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ },
+};
+
+static int ov5693_isp_configure(struct ov5693_device *sensor)
+{
+ int ret;
+
+ /* Enable lens correction. */
+ ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ OV5693_ISP_CTRL0_REG, 0x86);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ov5693_init(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (!dev->has_vcm)
+ return 0;
+
+ dev_info(&client->dev, "%s\n", __func__);
+ mutex_lock(&dev->input_lock);
+ dev->vcm_update = false;
+
+ if (dev->vcm == VCM_AD5823) {
+ ret = vcm_ad_i2c_wr8(client, 0x01, 0x01); /* vcm init test */
+ if (ret)
+ dev_err(&client->dev,
+ "vcm reset failed\n");
+ /*change the mode*/
+ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB,
+ AD5823_RING_CTRL_ENABLE);
+ if (ret)
+ dev_err(&client->dev,
+ "vcm enable ringing failed\n");
+ ret = ad5823_i2c_write(client, AD5823_REG_MODE,
+ AD5823_ARC_RES1);
+ if (ret)
+ dev_err(&client->dev,
+ "vcm change mode failed\n");
+ }
+
+ /*change initial focus value for ad5823*/
+ if (dev->vcm == VCM_AD5823) {
+ dev->focus = AD5823_INIT_FOCUS_POS;
+ ov5693_t_focus_abs(sd, AD5823_INIT_FOCUS_POS);
+ } else {
+ dev->focus = 0;
+ ov5693_t_focus_abs(sd, 0);
+ }
+
+ ov5693_isp_configure(dev);
+ mutex_unlock(&dev->input_lock);
+
+ return 0;
+}
+
+static int __power_up(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5693_device *sensor = to_ov5693_sensor(sd);
+ int ret;
+
+ ret = clk_prepare_enable(sensor->clk);
+ if (ret) {
+ dev_err(&client->dev, "Error enabling clock\n");
+ return -EINVAL;
+ }
+
+ if (sensor->indicator_led)
+ gpiod_set_value_cansleep(sensor->indicator_led, 1);
+
+ ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES,
+ sensor->supplies);
+ if (ret)
+ goto fail_power;
+
+ __cci_delay(up_delay);
+
+ return 0;
+
+fail_power:
+ if (sensor->indicator_led)
+ gpiod_set_value_cansleep(sensor->indicator_led, 0);
+ dev_err(&client->dev, "sensor power-up failed\n");
+
+ return ret;
+}
+
+static int power_down(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ dev->focus = OV5693_INVALID_CONFIG;
+
+ clk_disable_unprepare(dev->clk);
+
+ if (dev->indicator_led)
+ gpiod_set_value_cansleep(dev->indicator_led, 0);
+ return regulator_bulk_disable(OV5693_NUM_SUPPLIES, dev->supplies);
+}
+
+static int power_up(struct v4l2_subdev *sd)
+{
+ static const int retry_count = 4;
+ int i, ret;
+
+ for (i = 0; i < retry_count; i++) {
+ ret = __power_up(sd);
+ if (!ret)
+ return 0;
+
+ power_down(sd);
+ }
+ return ret;
+}
+
+static int ov5693_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ dev_info(&client->dev, "%s: on %d\n", __func__, on);
+
+ if (on == 0)
+ return power_down(sd);
+
+ /* on == 1 */
+ ret = power_up(sd);
+ if (!ret) {
+ ret = ov5693_init(sd);
+ /* restore settings */
+ ov5693_res = ov5693_res_video;
+ N_RES = N_RES_VIDEO;
+ }
+
+ return ret;
+}
+
+/*
+ * distance - calculate the distance
+ * @res: resolution
+ * @w: width
+ * @h: height
+ *
+ * Get the gap between res_w/res_h and w/h.
+ * distance = (res_w/res_h - w/h) / (w/h) * 8192
+ * res->width/height smaller than w/h wouldn't be considered.
+ * The gap of ratio larger than 1/8 wouldn't be considered.
+ * Returns the value of gap or -1 if fail.
+ */
+#define LARGEST_ALLOWED_RATIO_MISMATCH 1024
+static int distance(struct ov5693_resolution *res, u32 w, u32 h)
+{
+ int ratio;
+ int distance;
+
+ if (w == 0 || h == 0 ||
+ res->width < w || res->height < h)
+ return -1;
+
+ ratio = res->width << 13;
+ ratio /= w;
+ ratio *= h;
+ ratio /= res->height;
+
+ distance = abs(ratio - 8192);
+
+ if (distance > LARGEST_ALLOWED_RATIO_MISMATCH)
+ return -1;
+
+ return distance;
+}
+
+/* Return the nearest higher resolution index
+ * Firstly try to find the approximate aspect ratio resolution
+ * If we find multiple same AR resolutions, choose the
+ * minimal size.
+ */
+static int nearest_resolution_index(int w, int h)
+{
+ int i;
+ int idx = -1;
+ int dist;
+ int min_dist = INT_MAX;
+ int min_res_w = INT_MAX;
+ struct ov5693_resolution *tmp_res = NULL;
+
+ for (i = 0; i < N_RES; i++) {
+ tmp_res = &ov5693_res[i];
+ dist = distance(tmp_res, w, h);
+ if (dist == -1)
+ continue;
+ if (dist < min_dist) {
+ min_dist = dist;
+ idx = i;
+ min_res_w = ov5693_res[i].width;
+ continue;
+ }
+ if (dist == min_dist && ov5693_res[i].width < min_res_w)
+ idx = i;
+ }
+
+ return idx;
+}
+
+static int get_resolution_index(int w, int h)
+{
+ int i;
+
+ for (i = 0; i < N_RES; i++) {
+ if (w != ov5693_res[i].width)
+ continue;
+ if (h != ov5693_res[i].height)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+/* TODO: remove it. */
+static int startup(struct v4l2_subdev *sd)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ ret = ov5693_write_reg(client, OV5693_8BIT,
+ OV5693_SW_RESET, 0x01);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 reset err.\n");
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(client, ov5693_global_setting);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 write register err.\n");
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(client, ov5693_res[dev->fmt_idx].regs);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 write register err.\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ov5693_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+ int idx;
+ int cnt;
+
+ if (format->pad)
+ return -EINVAL;
+ if (!fmt)
+ return -EINVAL;
+
+ mutex_lock(&dev->input_lock);
+ idx = nearest_resolution_index(fmt->width, fmt->height);
+ if (idx == -1) {
+ /* return the largest resolution */
+ fmt->width = ov5693_res[N_RES - 1].width;
+ fmt->height = ov5693_res[N_RES - 1].height;
+ } else {
+ fmt->width = ov5693_res[idx].width;
+ fmt->height = ov5693_res[idx].height;
+ }
+
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ cfg->try_fmt = *fmt;
+ ret = 0;
+ goto mutex_unlock;
+ }
+
+ dev->fmt_idx = get_resolution_index(fmt->width, fmt->height);
+ if (dev->fmt_idx == -1) {
+ dev_err(&client->dev, "get resolution fail\n");
+ ret = -EINVAL;
+ goto mutex_unlock;
+ }
+
+ for (cnt = 0; cnt < OV5693_POWER_UP_RETRY_NUM; cnt++) {
+ ret = power_up(sd);
+ if (ret) {
+ dev_err(&client->dev, "power up failed\n");
+ continue;
+ }
+
+ mutex_unlock(&dev->input_lock);
+ ov5693_init(sd);
+ mutex_lock(&dev->input_lock);
+ ret = startup(sd);
+ if (ret)
+ dev_err(&client->dev, " startup() FAILED!\n");
+ else
+ break;
+ }
+ if (cnt == OV5693_POWER_UP_RETRY_NUM) {
+ dev_err(&client->dev, "power up failed, gave up\n");
+ goto mutex_unlock;
+ }
+
+
+
+ /*
+ * After sensor settings are set to HW, sometimes stream is started.
+ * This would cause ISP timeout because ISP is not ready to receive
+ * data yet. So add stop streaming here.
+ */
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
+ OV5693_STOP_STREAMING);
+ if (ret)
+ dev_warn(&client->dev, "ov5693 stream off err\n");
+
+mutex_unlock:
+ mutex_unlock(&dev->input_lock);
+ return ret;
+}
+
+static const struct v4l2_rect *
+__ov5693_get_pad_crop(struct ov5693_device *dev, 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(&dev->sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &dev->mode->crop;
+ }
+
+ return NULL;
+}
+static int ov5693_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP: {
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ mutex_lock(&dev->input_lock);
+ sel->r = *__ov5693_get_pad_crop(dev, cfg, sel->pad,
+ sel->which);
+ mutex_unlock(&dev->input_lock);
+
+ return 0;
+ }
+
+ 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;
+
+ return 0;
+
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.top = OV5693_PIXEL_ARRAY_TOP;
+ sel->r.left = OV5693_PIXEL_ARRAY_LEFT;
+ sel->r.width = OV5693_PIXEL_ARRAY_WIDTH;
+ sel->r.height = OV5693_PIXEL_ARRAY_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ov5693_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ if (format->pad)
+ return -EINVAL;
+
+ if (!fmt)
+ return -EINVAL;
+
+ fmt->width = ov5693_res[dev->fmt_idx].width;
+ fmt->height = ov5693_res[dev->fmt_idx].height;
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov5693_detect(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u16 high, low;
+ int ret;
+ u16 id;
+ u8 revision;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_SC_CMMN_CHIP_ID_H, &high);
+ if (ret) {
+ dev_err(&client->dev, "sensor_id_high = 0x%x\n", high);
+ return -ENODEV;
+ }
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_SC_CMMN_CHIP_ID_L, &low);
+ id = ((((u16)high) << 8) | (u16)low);
+
+ if (id != OV5693_ID) {
+ dev_err(&client->dev, "sensor ID error 0x%x\n", id);
+ return -ENODEV;
+ }
+
+ ret = ov5693_read_reg(client, OV5693_8BIT,
+ OV5693_SC_CMMN_SUB_ID, &high);
+ revision = (u8)high & 0x0f;
+
+ dev_info(&client->dev, "sensor_revision = 0x%x\n", revision);
+ dev_info(&client->dev, "sensor_address = 0x%02x\n", client->addr);
+ dev_info(&client->dev, "detect ov5693 success\n");
+ return 0;
+}
+
+static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ mutex_lock(&dev->input_lock);
+
+ /* power_on() here before streaming for regular PCs. */
+ if (enable) {
+ ret = power_up(sd);
+ if (ret) {
+ dev_err(&client->dev, "sensor power-up error\n");
+ goto out;
+ }
+ }
+
+ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
+ enable ? OV5693_START_STREAMING :
+ OV5693_STOP_STREAMING);
+
+ /* power_off() here after streaming for regular PCs. */
+ if (!enable)
+ power_down(sd);
+
+out:
+ mutex_unlock(&dev->input_lock);
+
+ return ret;
+}
+
+static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&dev->input_lock);
+ ret = power_up(sd);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 power-up err.\n");
+ goto fail_power_on;
+ }
+
+ if (!dev->vcm)
+ dev->vcm = vcm_detect(client);
+
+ /* config & detect sensor */
+ ret = ov5693_detect(client);
+ if (ret) {
+ dev_err(&client->dev, "ov5693_detect err s_config.\n");
+ goto fail_power_on;
+ }
+
+ dev->otp_data = ov5693_otp_read(sd);
+
+ /* turn off sensor, after probed */
+ ret = power_down(sd);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 power-off err.\n");
+ goto fail_power_on;
+ }
+ mutex_unlock(&dev->input_lock);
+
+ return ret;
+
+fail_power_on:
+ power_down(sd);
+ dev_err(&client->dev, "sensor power-gating failed\n");
+ mutex_unlock(&dev->input_lock);
+ return ret;
+}
+
+static int ov5693_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *interval)
+{
+ struct ov5693_device *dev = to_ov5693_sensor(sd);
+
+ interval->interval.numerator = 1;
+ interval->interval.denominator = ov5693_res[dev->fmt_idx].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)
+{
+ if (code->index >= MAX_FMTS)
+ 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)
+{
+ int index = fse->index;
+
+ if (index >= N_RES)
+ return -EINVAL;
+
+ fse->min_width = ov5693_res[index].width;
+ fse->min_height = ov5693_res[index].height;
+ fse->max_width = ov5693_res[index].width;
+ fse->max_height = ov5693_res[index].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_core_ops ov5693_core_ops = {
+ .s_power = ov5693_s_power,
+};
+
+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,
+};
+
+static const struct v4l2_subdev_ops ov5693_ops = {
+ .core = &ov5693_core_ops,
+ .video = &ov5693_video_ops,
+ .pad = &ov5693_pad_ops,
+};
+
+static int ov5693_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ unsigned int i = OV5693_NUM_SUPPLIES;
+
+ dev_info(&client->dev, "%s...\n", __func__);
+
+ gpiod_put(ov5693->reset);
+ gpiod_put(ov5693->indicator_led);
+ while (i--)
+ regulator_put(ov5693->supplies[i].consumer);
+
+ v4l2_async_unregister_subdev(sd);
+
+ media_entity_cleanup(&ov5693->sd.entity);
+ v4l2_ctrl_handler_free(&ov5693->ctrl_handler);
+ kfree(ov5693);
+
+ return 0;
+}
+
+static int ov5693_init_controls(struct ov5693_device *ov5693)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5693->sd);
+ const struct v4l2_ctrl_ops *ops = &ov5693_ctrl_ops;
+ struct v4l2_ctrl *ctrl;
+ unsigned int i;
+ int ret;
+ int hblank;
+
+ ret = v4l2_ctrl_handler_init(&ov5693->ctrl_handler,
+ ARRAY_SIZE(ov5693_controls));
+ if (ret) {
+ ov5693_remove(client);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ov5693_controls); i++)
+ v4l2_ctrl_new_custom(&ov5693->ctrl_handler,
+ &ov5693_controls[i],
+ NULL);
+
+ /* link freq */
+ ctrl = v4l2_ctrl_new_int_menu(&ov5693->ctrl_handler, NULL,
+ V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* pixel rate */
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, OV5693_PIXEL_RATE, 1, OV5693_PIXEL_RATE);
+
+ if (ov5693->ctrl_handler.error) {
+ ov5693_remove(client);
+ return ov5693->ctrl_handler.error;
+ }
+
+ /* Exposure */
+
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16,
+ 512);
+
+ /* Gain */
+
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_ANALOGUE_GAIN, 1, 1023, 1, 128);
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_DIGITAL_GAIN, 1, 3999, 1, 1000);
+
+ /* Flip */
+
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ hblank = OV5693_PPL_DEFAULT - ov5693->mode->width;
+ ov5693->hblank = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_HBLANK, hblank, hblank,
+ 1, hblank);
+ if (ov5693->hblank)
+ ov5693->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* Use same lock for controls as for everything else. */
+ ov5693->ctrl_handler.lock = &ov5693->input_lock;
+ ov5693->sd.ctrl_handler = &ov5693->ctrl_handler;
+
+ return 0;
+}
+
+static int ov5693_configure_gpios(struct ov5693_device *ov5693)
+{
+ ov5693->reset = gpiod_get_index(&ov5693->i2c_client->dev, "reset", 0,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5693->reset)) {
+ dev_err(&ov5693->i2c_client->dev, "Couldn't find reset GPIO\n");
+ return -EINVAL;
+ }
+
+ ov5693->indicator_led = gpiod_get_index_optional(&ov5693->i2c_client->dev, "indicator-led", 0,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5693->indicator_led)) {
+ dev_err(&ov5693->i2c_client->dev, "Couldn't find indicator-led GPIO\n");
+ return -EINVAL;
+ }
+
+ 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 regulator_bulk_get(&ov5693->i2c_client->dev,
+ OV5693_NUM_SUPPLIES,
+ ov5693->supplies);
+}
+
+static int ov5693_probe(struct i2c_client *client)
+{
+ struct ov5693_device *ov5693;
+ int ret = 0;
+
+ dev_info(&client->dev, "%s() called", __func__);
+
+ ov5693 = kzalloc(sizeof(*ov5693), GFP_KERNEL);
+ if (!ov5693)
+ return -ENOMEM;
+
+ ov5693->i2c_client = client;
+
+ /* check if VCM device exists */
+ /* TODO: read from SSDB */
+ ov5693->has_vcm = false;
+
+ mutex_init(&ov5693->input_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 -EINVAL;
+ }
+
+ ret = ov5693_configure_gpios(ov5693);
+ if (ret)
+ goto out_free;
+
+ ret = ov5693_get_regulators(ov5693);
+ if (ret)
+ goto out_put_reset;
+
+ ret = ov5693_s_config(&ov5693->sd, client->irq);
+ if (ret)
+ goto out_put_reset;
+
+ ov5693->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov5693->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov5693->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ ov5693->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ov5693->mode = &ov5693_res_video[N_RES_VIDEO-1];
+
+ ret = ov5693_init_controls(ov5693);
+ if (ret)
+ ov5693_remove(client);
+
+ ret = media_entity_pads_init(&ov5693->sd.entity, 1, &ov5693->pad);
+ if (ret)
+ ov5693_remove(client);
+
+ ret = v4l2_async_register_subdev_sensor_common(&ov5693->sd);
+ if (ret) {
+ dev_err(&client->dev, "failed to register V4L2 subdev: %d", ret);
+ goto media_entity_cleanup;
+ }
+
+ return ret;
+
+media_entity_cleanup:
+ media_entity_cleanup(&ov5693->sd.entity);
+out_put_reset:
+ gpiod_put(ov5693->reset);
+out_free:
+ v4l2_device_unregister_subdev(&ov5693->sd);
+ kfree(ov5693);
+ return ret;
+}
+
+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,
+ },
+ .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");
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
new file mode 100644
index 000000000000..9a508e1f3624
--- /dev/null
+++ b/drivers/media/i2c/ov5693.h
@@ -0,0 +1,1430 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Support for OmniVision OV5693 5M camera sensor.
+ *
+ * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#ifndef __OV5693_H__
+#define __OV5693_H__
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/media-entity.h>
+
+#define OV5693_HID "INT33BE"
+
+/*
+ * FIXME: non-preview resolutions are currently broken
+ */
+#define ENABLE_NON_PREVIEW 1
+
+#define OV5693_POWER_UP_RETRY_NUM 5
+
+/* Defines for register writes and register array processing */
+#define I2C_MSG_LENGTH 0x2
+#define I2C_RETRY_COUNT 5
+
+#define OV5693_FOCAL_LENGTH_NUM 334 /*3.34mm*/
+#define OV5693_FOCAL_LENGTH_DEM 100
+#define OV5693_F_NUMBER_DEFAULT_NUM 24
+#define OV5693_F_NUMBER_DEM 10
+
+#define MAX_FMTS 1
+
+/* sensor_mode_data read_mode adaptation */
+#define OV5693_READ_MODE_BINNING_ON 0x0400
+#define OV5693_READ_MODE_BINNING_OFF 0x00
+#define OV5693_INTEGRATION_TIME_MARGIN 8
+
+#define OV5693_MAX_EXPOSURE_VALUE 0xFFF1
+#define OV5693_MAX_GAIN_VALUE 0xFF
+
+/*
+ * focal length bits definition:
+ * bits 31-16: numerator, bits 15-0: denominator
+ */
+#define OV5693_FOCAL_LENGTH_DEFAULT 0x1B70064
+
+/*
+ * current f-number bits definition:
+ * bits 31-16: numerator, bits 15-0: denominator
+ */
+#define OV5693_F_NUMBER_DEFAULT 0x18000a
+
+/*
+ * f-number range bits definition:
+ * bits 31-24: max f-number numerator
+ * bits 23-16: max f-number denominator
+ * bits 15-8: min f-number numerator
+ * bits 7-0: min f-number denominator
+ */
+#define OV5693_F_NUMBER_RANGE 0x180a180a
+#define OV5693_ID 0x5690
+
+#define OV5693_FINE_INTG_TIME_MIN 0
+#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0
+#define OV5693_COARSE_INTG_TIME_MIN 1
+#define OV5693_COARSE_INTG_TIME_MAX_MARGIN 6
+
+#define OV5693_BIN_FACTOR_MAX 4
+/*
+ * OV5693 System control registers
+ */
+#define OV5693_SW_SLEEP 0x0100
+#define OV5693_SW_RESET 0x0103
+#define OV5693_SW_STREAM 0x0100
+
+#define OV5693_SC_CMMN_CHIP_ID_H 0x300A
+#define OV5693_SC_CMMN_CHIP_ID_L 0x300B
+#define OV5693_SC_CMMN_SCCB_ID 0x300C
+#define OV5693_SC_CMMN_SUB_ID 0x302A /* process, version*/
+/*Bit[7:4] Group control, Bit[3:0] Group ID*/
+#define OV5693_GROUP_ACCESS 0x3208
+/*
+*Bit[3:0] Bit[19:16] of exposure,
+*remaining 16 bits lies in Reg0x3501&Reg0x3502
+*/
+#define OV5693_EXPOSURE_H 0x3500
+#define OV5693_EXPOSURE_M 0x3501
+#define OV5693_EXPOSURE_L 0x3502
+/*Bit[1:0] means Bit[9:8] of gain*/
+#define OV5693_AGC_H 0x350A
+#define OV5693_AGC_L 0x350B /*Bit[7:0] of gain*/
+
+#define OV5693_HORIZONTAL_START_H 0x3800 /*Bit[11:8]*/
+#define OV5693_HORIZONTAL_START_L 0x3801 /*Bit[7:0]*/
+#define OV5693_VERTICAL_START_H 0x3802 /*Bit[11:8]*/
+#define OV5693_VERTICAL_START_L 0x3803 /*Bit[7:0]*/
+#define OV5693_HORIZONTAL_END_H 0x3804 /*Bit[11:8]*/
+#define OV5693_HORIZONTAL_END_L 0x3805 /*Bit[7:0]*/
+#define OV5693_VERTICAL_END_H 0x3806 /*Bit[11:8]*/
+#define OV5693_VERTICAL_END_L 0x3807 /*Bit[7:0]*/
+#define OV5693_HORIZONTAL_OUTPUT_SIZE_H 0x3808 /*Bit[3:0]*/
+#define OV5693_HORIZONTAL_OUTPUT_SIZE_L 0x3809 /*Bit[7:0]*/
+#define OV5693_VERTICAL_OUTPUT_SIZE_H 0x380a /*Bit[3:0]*/
+#define OV5693_VERTICAL_OUTPUT_SIZE_L 0x380b /*Bit[7:0]*/
+/*High 8-bit, and low 8-bit HTS address is 0x380d*/
+#define OV5693_TIMING_HTS_H 0x380C
+/*High 8-bit, and low 8-bit HTS address is 0x380d*/
+#define OV5693_TIMING_HTS_L 0x380D
+/*High 8-bit, and low 8-bit HTS address is 0x380f*/
+#define OV5693_TIMING_VTS_H 0x380e
+/*High 8-bit, and low 8-bit HTS address is 0x380f*/
+#define OV5693_TIMING_VTS_L 0x380f
+
+#define OV5693_MWB_RED_GAIN_H 0x3400
+#define OV5693_MWB_GREEN_GAIN_H 0x3402
+#define OV5693_MWB_BLUE_GAIN_H 0x3404
+#define OV5693_MWB_GAIN_MAX 0x0fff
+
+#define OV5693_START_STREAMING 0x01
+#define OV5693_STOP_STREAMING 0x00
+
+#define VCM_ADDR 0x0c
+#define VCM_CODE_MSB 0x04
+
+#define OV5693_INVALID_CONFIG 0xffffffff
+
+#define OV5693_VCM_SLEW_STEP 0x30F0
+#define OV5693_VCM_SLEW_STEP_MAX 0x7
+#define OV5693_VCM_SLEW_STEP_MASK 0x7
+#define OV5693_VCM_CODE 0x30F2
+#define OV5693_VCM_SLEW_TIME 0x30F4
+#define OV5693_VCM_SLEW_TIME_MAX 0xffff
+#define OV5693_VCM_ENABLE 0x8000
+
+#define OV5693_VCM_MAX_FOCUS_NEG -1023
+#define OV5693_VCM_MAX_FOCUS_POS 1023
+
+#define DLC_ENABLE 1
+#define DLC_DISABLE 0
+#define VCM_PROTECTION_OFF 0xeca3
+#define VCM_PROTECTION_ON 0xdc51
+#define VCM_DEFAULT_S 0x0
+#define vcm_step_s(a) (u8)(a & 0xf)
+#define vcm_step_mclk(a) (u8)((a >> 4) & 0x3)
+#define vcm_dlc_mclk(dlc, mclk) (u16)((dlc << 3) | mclk | 0xa104)
+#define vcm_tsrc(tsrc) (u16)(tsrc << 3 | 0xf200)
+#define vcm_val(data, s) (u16)(data << 4 | s)
+#define DIRECT_VCM vcm_dlc_mclk(0, 0)
+
+/* Defines for OTP Data Registers */
+#define OV5693_FRAME_OFF_NUM 0x4202
+#define OV5693_OTP_BYTE_MAX 32 //change to 32 as needed by otpdata
+#define OV5693_OTP_SHORT_MAX 16
+#define OV5693_OTP_START_ADDR 0x3D00
+#define OV5693_OTP_END_ADDR 0x3D0F
+#define OV5693_OTP_DATA_SIZE 320
+#define OV5693_OTP_PROGRAM_REG 0x3D80
+#define OV5693_OTP_READ_REG 0x3D81 // 1:Enable 0:disable
+#define OV5693_OTP_BANK_REG 0x3D84 //otp bank and mode
+#define OV5693_OTP_READY_REG_DONE 1
+#define OV5693_OTP_BANK_MAX 28
+#define OV5693_OTP_BANK_SIZE 16 //16 bytes per bank
+#define OV5693_OTP_READ_ONETIME 16
+#define OV5693_OTP_MODE_READ 1
+
+/* link freq and pixel rate required for IPU3 */
+#define OV5693_LINK_FREQ_640MHZ 640000000
+/* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample
+ * To avoid integer overflow, dividing by bits_per_sample first.
+ */
+#define OV5693_PIXEL_RATE (OV5693_LINK_FREQ_640MHZ / 10) * 2 * 2
+static const s64 link_freq_menu_items[] = {
+ OV5693_LINK_FREQ_640MHZ
+};
+
+#define OV5693_NUM_SUPPLIES 2
+static const char * const ov5693_supply_names[] = {
+ "avdd",
+ "dovdd",
+};
+
+struct regval_list {
+ u16 reg_num;
+ u8 value;
+};
+
+struct ov5693_resolution {
+ u8 *desc;
+ const struct ov5693_reg *regs;
+ int res;
+ int width;
+ int height;
+ int fps;
+ int pix_clk_freq;
+ u16 pixels_per_line;
+ u16 lines_per_frame;
+ u8 bin_factor_x;
+ u8 bin_factor_y;
+ u8 bin_mode;
+ bool used;
+
+ /* Analog crop rectangle. */
+ struct v4l2_rect crop;
+};
+
+struct ov5693_format {
+ u8 *desc;
+ u32 pixelformat;
+ struct ov5693_reg *regs;
+};
+
+enum vcm_type {
+ VCM_UNKNOWN,
+ VCM_AD5823,
+ VCM_DW9714,
+};
+
+/*
+ * ov5693 device structure.
+ */
+struct ov5693_device {
+ struct i2c_client *i2c_client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt format;
+ struct mutex input_lock;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct gpio_desc *reset;
+ struct gpio_desc *indicator_led;
+ struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES];
+ struct clk *clk;
+
+ /* Current mode */
+ const struct ov5693_resolution *mode;
+
+ struct camera_sensor_platform_data *platform_data;
+ ktime_t timestamp_t_focus_abs;
+ int vt_pix_clk_freq_mhz;
+ int fmt_idx;
+ int run_mode;
+ int otp_size;
+ u8 *otp_data;
+ u32 focus;
+ s16 number_of_steps;
+ u8 res;
+ u8 type;
+ bool vcm_update;
+ enum vcm_type vcm;
+
+ bool has_vcm;
+
+ struct v4l2_ctrl *hblank;
+};
+
+enum ov5693_tok_type {
+ OV5693_8BIT = 0x0001,
+ OV5693_16BIT = 0x0002,
+ OV5693_32BIT = 0x0004,
+ OV5693_TOK_TERM = 0xf000, /* terminating token for reg list */
+ OV5693_TOK_DELAY = 0xfe00, /* delay token for reg list */
+ OV5693_TOK_MASK = 0xfff0
+};
+
+/**
+ * struct ov5693_reg - MI sensor register format
+ * @type: type of the register
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ *
+ * Define a structure for sensor register initialization values
+ */
+struct ov5693_reg {
+ enum ov5693_tok_type type;
+ u16 reg;
+ u32 val; /* @set value for read/mod/write, @mask */
+};
+
+#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd)
+
+#define OV5693_MAX_WRITE_BUF_SIZE 30
+
+struct ov5693_write_buffer {
+ u16 addr;
+ u8 data[OV5693_MAX_WRITE_BUF_SIZE];
+};
+
+struct ov5693_write_ctrl {
+ int index;
+ struct ov5693_write_buffer buffer;
+};
+
+static struct ov5693_reg const ov5693_global_setting[] = {
+ {OV5693_8BIT, 0x0103, 0x01},
+ {OV5693_8BIT, 0x3001, 0x0a},
+ {OV5693_8BIT, 0x3002, 0x80},
+ {OV5693_8BIT, 0x3006, 0x00},
+ {OV5693_8BIT, 0x3011, 0x21},
+ {OV5693_8BIT, 0x3012, 0x09},
+ {OV5693_8BIT, 0x3013, 0x10},
+ {OV5693_8BIT, 0x3014, 0x00},
+ {OV5693_8BIT, 0x3015, 0x08},
+ {OV5693_8BIT, 0x3016, 0xf0},
+ {OV5693_8BIT, 0x3017, 0xf0},
+ {OV5693_8BIT, 0x3018, 0xf0},
+ {OV5693_8BIT, 0x301b, 0xb4},
+ {OV5693_8BIT, 0x301d, 0x02},
+ {OV5693_8BIT, 0x3021, 0x00},
+ {OV5693_8BIT, 0x3022, 0x01},
+ {OV5693_8BIT, 0x3028, 0x44},
+ {OV5693_8BIT, 0x3098, 0x02},
+ {OV5693_8BIT, 0x3099, 0x19},
+ {OV5693_8BIT, 0x309a, 0x02},
+ {OV5693_8BIT, 0x309b, 0x01},
+ {OV5693_8BIT, 0x309c, 0x00},
+ {OV5693_8BIT, 0x30a0, 0xd2},
+ {OV5693_8BIT, 0x30a2, 0x01},
+ {OV5693_8BIT, 0x30b2, 0x00},
+ {OV5693_8BIT, 0x30b3, 0x7d},
+ {OV5693_8BIT, 0x30b4, 0x03},
+ {OV5693_8BIT, 0x30b5, 0x04},
+ {OV5693_8BIT, 0x30b6, 0x01},
+ {OV5693_8BIT, 0x3104, 0x21},
+ {OV5693_8BIT, 0x3106, 0x00},
+ {OV5693_8BIT, 0x3400, 0x04},
+ {OV5693_8BIT, 0x3401, 0x00},
+ {OV5693_8BIT, 0x3402, 0x04},
+ {OV5693_8BIT, 0x3403, 0x00},
+ {OV5693_8BIT, 0x3404, 0x04},
+ {OV5693_8BIT, 0x3405, 0x00},
+ {OV5693_8BIT, 0x3406, 0x01},
+ {OV5693_8BIT, 0x3500, 0x00},
+ {OV5693_8BIT, 0x3503, 0x07},
+ {OV5693_8BIT, 0x3504, 0x00},
+ {OV5693_8BIT, 0x3505, 0x00},
+ {OV5693_8BIT, 0x3506, 0x00},
+ {OV5693_8BIT, 0x3507, 0x02},
+ {OV5693_8BIT, 0x3508, 0x00},
+ {OV5693_8BIT, 0x3509, 0x10},
+ {OV5693_8BIT, 0x350a, 0x00},
+ {OV5693_8BIT, 0x350b, 0x40},
+ {OV5693_8BIT, 0x3601, 0x0a},
+ {OV5693_8BIT, 0x3602, 0x38},
+ {OV5693_8BIT, 0x3612, 0x80},
+ {OV5693_8BIT, 0x3620, 0x54},
+ {OV5693_8BIT, 0x3621, 0xc7},
+ {OV5693_8BIT, 0x3622, 0x0f},
+ {OV5693_8BIT, 0x3625, 0x10},
+ {OV5693_8BIT, 0x3630, 0x55},
+ {OV5693_8BIT, 0x3631, 0xf4},
+ {OV5693_8BIT, 0x3632, 0x00},
+ {OV5693_8BIT, 0x3633, 0x34},
+ {OV5693_8BIT, 0x3634, 0x02},
+ {OV5693_8BIT, 0x364d, 0x0d},
+ {OV5693_8BIT, 0x364f, 0xdd},
+ {OV5693_8BIT, 0x3660, 0x04},
+ {OV5693_8BIT, 0x3662, 0x10},
+ {OV5693_8BIT, 0x3663, 0xf1},
+ {OV5693_8BIT, 0x3665, 0x00},
+ {OV5693_8BIT, 0x3666, 0x20},
+ {OV5693_8BIT, 0x3667, 0x00},
+ {OV5693_8BIT, 0x366a, 0x80},
+ {OV5693_8BIT, 0x3680, 0xe0},
+ {OV5693_8BIT, 0x3681, 0x00},
+ {OV5693_8BIT, 0x3700, 0x42},
+ {OV5693_8BIT, 0x3701, 0x14},
+ {OV5693_8BIT, 0x3702, 0xa0},
+ {OV5693_8BIT, 0x3703, 0xd8},
+ {OV5693_8BIT, 0x3704, 0x78},
+ {OV5693_8BIT, 0x3705, 0x02},
+ {OV5693_8BIT, 0x370a, 0x00},
+ {OV5693_8BIT, 0x370b, 0x20},
+ {OV5693_8BIT, 0x370c, 0x0c},
+ {OV5693_8BIT, 0x370d, 0x11},
+ {OV5693_8BIT, 0x370e, 0x00},
+ {OV5693_8BIT, 0x370f, 0x40},
+ {OV5693_8BIT, 0x3710, 0x00},
+ {OV5693_8BIT, 0x371a, 0x1c},
+ {OV5693_8BIT, 0x371b, 0x05},
+ {OV5693_8BIT, 0x371c, 0x01},
+ {OV5693_8BIT, 0x371e, 0xa1},
+ {OV5693_8BIT, 0x371f, 0x0c},
+ {OV5693_8BIT, 0x3721, 0x00},
+ {OV5693_8BIT, 0x3724, 0x10},
+ {OV5693_8BIT, 0x3726, 0x00},
+ {OV5693_8BIT, 0x372a, 0x01},
+ {OV5693_8BIT, 0x3730, 0x10},
+ {OV5693_8BIT, 0x3738, 0x22},
+ {OV5693_8BIT, 0x3739, 0xe5},
+ {OV5693_8BIT, 0x373a, 0x50},
+ {OV5693_8BIT, 0x373b, 0x02},
+ {OV5693_8BIT, 0x373c, 0x41},
+ {OV5693_8BIT, 0x373f, 0x02},
+ {OV5693_8BIT, 0x3740, 0x42},
+ {OV5693_8BIT, 0x3741, 0x02},
+ {OV5693_8BIT, 0x3742, 0x18},
+ {OV5693_8BIT, 0x3743, 0x01},
+ {OV5693_8BIT, 0x3744, 0x02},
+ {OV5693_8BIT, 0x3747, 0x10},
+ {OV5693_8BIT, 0x374c, 0x04},
+ {OV5693_8BIT, 0x3751, 0xf0},
+ {OV5693_8BIT, 0x3752, 0x00},
+ {OV5693_8BIT, 0x3753, 0x00},
+ {OV5693_8BIT, 0x3754, 0xc0},
+ {OV5693_8BIT, 0x3755, 0x00},
+ {OV5693_8BIT, 0x3756, 0x1a},
+ {OV5693_8BIT, 0x3758, 0x00},
+ {OV5693_8BIT, 0x3759, 0x0f},
+ {OV5693_8BIT, 0x376b, 0x44},
+ {OV5693_8BIT, 0x375c, 0x04},
+ {OV5693_8BIT, 0x3774, 0x10},
+ {OV5693_8BIT, 0x3776, 0x00},
+ {OV5693_8BIT, 0x377f, 0x08},
+ {OV5693_8BIT, 0x3780, 0x22},
+ {OV5693_8BIT, 0x3781, 0x0c},
+ {OV5693_8BIT, 0x3784, 0x2c},
+ {OV5693_8BIT, 0x3785, 0x1e},
+ {OV5693_8BIT, 0x378f, 0xf5},
+ {OV5693_8BIT, 0x3791, 0xb0},
+ {OV5693_8BIT, 0x3795, 0x00},
+ {OV5693_8BIT, 0x3796, 0x64},
+ {OV5693_8BIT, 0x3797, 0x11},
+ {OV5693_8BIT, 0x3798, 0x30},
+ {OV5693_8BIT, 0x3799, 0x41},
+ {OV5693_8BIT, 0x379a, 0x07},
+ {OV5693_8BIT, 0x379b, 0xb0},
+ {OV5693_8BIT, 0x379c, 0x0c},
+ {OV5693_8BIT, 0x37c5, 0x00},
+ {OV5693_8BIT, 0x37c6, 0x00},
+ {OV5693_8BIT, 0x37c7, 0x00},
+ {OV5693_8BIT, 0x37c9, 0x00},
+ {OV5693_8BIT, 0x37ca, 0x00},
+ {OV5693_8BIT, 0x37cb, 0x00},
+ {OV5693_8BIT, 0x37de, 0x00},
+ {OV5693_8BIT, 0x37df, 0x00},
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3810, 0x00},
+ {OV5693_8BIT, 0x3812, 0x00},
+ {OV5693_8BIT, 0x3823, 0x00},
+ {OV5693_8BIT, 0x3824, 0x00},
+ {OV5693_8BIT, 0x3825, 0x00},
+ {OV5693_8BIT, 0x3826, 0x00},
+ {OV5693_8BIT, 0x3827, 0x00},
+ {OV5693_8BIT, 0x382a, 0x04},
+ {OV5693_8BIT, 0x3a04, 0x06},
+ {OV5693_8BIT, 0x3a05, 0x14},
+ {OV5693_8BIT, 0x3a06, 0x00},
+ {OV5693_8BIT, 0x3a07, 0xfe},
+ {OV5693_8BIT, 0x3b00, 0x00},
+ {OV5693_8BIT, 0x3b02, 0x00},
+ {OV5693_8BIT, 0x3b03, 0x00},
+ {OV5693_8BIT, 0x3b04, 0x00},
+ {OV5693_8BIT, 0x3b05, 0x00},
+ {OV5693_8BIT, 0x3e07, 0x20},
+ {OV5693_8BIT, 0x4000, 0x08},
+ {OV5693_8BIT, 0x4001, 0x04},
+ {OV5693_8BIT, 0x4002, 0x45},
+ {OV5693_8BIT, 0x4004, 0x08},
+ {OV5693_8BIT, 0x4005, 0x18},
+ {OV5693_8BIT, 0x4006, 0x20},
+ {OV5693_8BIT, 0x4008, 0x24},
+ {OV5693_8BIT, 0x4009, 0x10},
+ {OV5693_8BIT, 0x400c, 0x00},
+ {OV5693_8BIT, 0x400d, 0x00},
+ {OV5693_8BIT, 0x4058, 0x00},
+ {OV5693_8BIT, 0x404e, 0x37},
+ {OV5693_8BIT, 0x404f, 0x8f},
+ {OV5693_8BIT, 0x4058, 0x00},
+ {OV5693_8BIT, 0x4101, 0xb2},
+ {OV5693_8BIT, 0x4303, 0x00},
+ {OV5693_8BIT, 0x4304, 0x08},
+ {OV5693_8BIT, 0x4307, 0x31},
+ {OV5693_8BIT, 0x4311, 0x04},
+ {OV5693_8BIT, 0x4315, 0x01},
+ {OV5693_8BIT, 0x4511, 0x05},
+ {OV5693_8BIT, 0x4512, 0x01},
+ {OV5693_8BIT, 0x4806, 0x00},
+ {OV5693_8BIT, 0x4816, 0x52},
+ {OV5693_8BIT, 0x481f, 0x30},
+ {OV5693_8BIT, 0x4826, 0x2c},
+ {OV5693_8BIT, 0x4831, 0x64},
+ {OV5693_8BIT, 0x4d00, 0x04},
+ {OV5693_8BIT, 0x4d01, 0x71},
+ {OV5693_8BIT, 0x4d02, 0xfd},
+ {OV5693_8BIT, 0x4d03, 0xf5},
+ {OV5693_8BIT, 0x4d04, 0x0c},
+ {OV5693_8BIT, 0x4d05, 0xcc},
+ {OV5693_8BIT, 0x4837, 0x0a},
+ {OV5693_8BIT, 0x5000, 0x06},
+ {OV5693_8BIT, 0x5001, 0x01},
+ {OV5693_8BIT, 0x5003, 0x20},
+ {OV5693_8BIT, 0x5046, 0x0a},
+ {OV5693_8BIT, 0x5013, 0x00},
+ {OV5693_8BIT, 0x5046, 0x0a},
+ {OV5693_8BIT, 0x5780, 0x1c},
+ {OV5693_8BIT, 0x5786, 0x20},
+ {OV5693_8BIT, 0x5787, 0x10},
+ {OV5693_8BIT, 0x5788, 0x18},
+ {OV5693_8BIT, 0x578a, 0x04},
+ {OV5693_8BIT, 0x578b, 0x02},
+ {OV5693_8BIT, 0x578c, 0x02},
+ {OV5693_8BIT, 0x578e, 0x06},
+ {OV5693_8BIT, 0x578f, 0x02},
+ {OV5693_8BIT, 0x5790, 0x02},
+ {OV5693_8BIT, 0x5791, 0xff},
+ {OV5693_8BIT, 0x5842, 0x01},
+ {OV5693_8BIT, 0x5843, 0x2b},
+ {OV5693_8BIT, 0x5844, 0x01},
+ {OV5693_8BIT, 0x5845, 0x92},
+ {OV5693_8BIT, 0x5846, 0x01},
+ {OV5693_8BIT, 0x5847, 0x8f},
+ {OV5693_8BIT, 0x5848, 0x01},
+ {OV5693_8BIT, 0x5849, 0x0c},
+ {OV5693_8BIT, 0x5e00, 0x00},
+ {OV5693_8BIT, 0x5e10, 0x0c},
+ {OV5693_8BIT, 0x0100, 0x00},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+#if ENABLE_NON_PREVIEW
+/*
+ * 654x496 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ */
+static struct ov5693_reg const ov5693_654x496[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x02},
+ {OV5693_8BIT, 0x3809, 0x90},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0xf0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x08},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 1296x976 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+*DS from 2592x1952
+*/
+static struct ov5693_reg const ov5693_1296x976[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0x00},
+
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xA3},
+
+ {OV5693_8BIT, 0x3808, 0x05},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x03},
+ {OV5693_8BIT, 0x380b, 0xD0},
+
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+
+ {OV5693_8BIT, 0x3810, 0x00},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3812, 0x00},
+ {OV5693_8BIT, 0x3813, 0x02},
+
+ {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
+ {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+
+};
+
+/*
+ * 336x256 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ DS from 2564x1956
+ */
+static struct ov5693_reg const ov5693_336x256[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x01},
+ {OV5693_8BIT, 0x3809, 0x50},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0x00},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x1E},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 336x256 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ DS from 2368x1956
+ */
+static struct ov5693_reg const ov5693_368x304[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3808, 0x01},
+ {OV5693_8BIT, 0x3809, 0x70},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0x30},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x80},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * ov5693_192x160 30fps 17ms VBlanking 2lane 10Bit (Scaling)
+ DS from 2460x1956
+ */
+static struct ov5693_reg const ov5693_192x160[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x80},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xA3},
+ {OV5693_8BIT, 0x3808, 0x00},
+ {OV5693_8BIT, 0x3809, 0xC0},
+ {OV5693_8BIT, 0x380a, 0x00},
+ {OV5693_8BIT, 0x380b, 0xA0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x40},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_736x496[] = {
+ {OV5693_8BIT, 0x3501, 0x3d},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc7},
+ {OV5693_8BIT, 0x3803, 0x68},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0x3b},
+ {OV5693_8BIT, 0x3808, 0x02},
+ {OV5693_8BIT, 0x3809, 0xe0},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0xf0},
+ {OV5693_8BIT, 0x380c, 0x0a}, /*hts*/
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /*vts*/
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x08},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x04},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+/*
+static struct ov5693_reg const ov5693_736x496[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe6},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x02},
+ {OV5693_8BIT, 0x3809, 0xe0},
+ {OV5693_8BIT, 0x380a, 0x01},
+ {OV5693_8BIT, 0x380b, 0xf0},
+ {OV5693_8BIT, 0x380c, 0x0d},
+ {OV5693_8BIT, 0x380d, 0xb0},
+ {OV5693_8BIT, 0x380e, 0x05},
+ {OV5693_8BIT, 0x380f, 0xf2},
+ {OV5693_8BIT, 0x3811, 0x08},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x31},
+ {OV5693_8BIT, 0x3815, 0x31},
+ {OV5693_8BIT, 0x3820, 0x01},
+ {OV5693_8BIT, 0x3821, 0x1f},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+*/
+/*
+ * 976x556 30fps 8.8ms VBlanking 2lane 10Bit (Scaling)
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_976x556[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa7},
+ {OV5693_8BIT, 0x3808, 0x03},
+ {OV5693_8BIT, 0x3809, 0xd0},
+ {OV5693_8BIT, 0x380a, 0x02},
+ {OV5693_8BIT, 0x380b, 0x2C},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*DS from 2624x1492*/
+static struct ov5693_reg const ov5693_1296x736[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0x00},
+
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xA3},
+
+ {OV5693_8BIT, 0x3808, 0x05},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x02},
+ {OV5693_8BIT, 0x380b, 0xe0},
+
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+
+ {OV5693_8BIT, 0x3813, 0xE8},
+
+ {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
+ {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_1636p_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa7},
+ {OV5693_8BIT, 0x3808, 0x06},
+ {OV5693_8BIT, 0x3809, 0x64},
+ {OV5693_8BIT, 0x380a, 0x04},
+ {OV5693_8BIT, 0x380b, 0x48},
+ {OV5693_8BIT, 0x380c, 0x0a}, /*hts*/
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /*vts*/
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x02},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+static struct ov5693_reg const ov5693_1616x1216_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x80},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00}, /*{3800,3801} Array X start*/
+ {OV5693_8BIT, 0x3801, 0x08}, /* 04 //{3800,3801} Array X start*/
+ {OV5693_8BIT, 0x3802, 0x00}, /*{3802,3803} Array Y start*/
+ {OV5693_8BIT, 0x3803, 0x04}, /* 00 //{3802,3803} Array Y start*/
+ {OV5693_8BIT, 0x3804, 0x0a}, /*{3804,3805} Array X end*/
+ {OV5693_8BIT, 0x3805, 0x37}, /* 3b //{3804,3805} Array X end*/
+ {OV5693_8BIT, 0x3806, 0x07}, /*{3806,3807} Array Y end*/
+ {OV5693_8BIT, 0x3807, 0x9f}, /* a3 //{3806,3807} Array Y end*/
+ {OV5693_8BIT, 0x3808, 0x06}, /*{3808,3809} Final output H size*/
+ {OV5693_8BIT, 0x3809, 0x50}, /*{3808,3809} Final output H size*/
+ {OV5693_8BIT, 0x380a, 0x04}, /*{380a,380b} Final output V size*/
+ {OV5693_8BIT, 0x380b, 0xc0}, /*{380a,380b} Final output V size*/
+ {OV5693_8BIT, 0x380c, 0x0a}, /*{380c,380d} HTS*/
+ {OV5693_8BIT, 0x380d, 0x80}, /*{380c,380d} HTS*/
+ {OV5693_8BIT, 0x380e, 0x07}, /*{380e,380f} VTS*/
+ {OV5693_8BIT, 0x380f, 0xc0}, /* bc //{380e,380f} VTS*/
+ {OV5693_8BIT, 0x3810, 0x00}, /*{3810,3811} windowing X offset*/
+ {OV5693_8BIT, 0x3811, 0x10}, /*{3810,3811} windowing X offset*/
+ {OV5693_8BIT, 0x3812, 0x00}, /*{3812,3813} windowing Y offset*/
+ {OV5693_8BIT, 0x3813, 0x06}, /*{3812,3813} windowing Y offset*/
+ {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
+ {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
+ {OV5693_8BIT, 0x3820, 0x00}, /*FLIP/Binnning control*/
+ {OV5693_8BIT, 0x3821, 0x1e}, /*MIRROR control*/
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 1940x1096 30fps 8.8ms VBlanking 2lane 10bit (Scaling)
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_1940x1096[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa7},
+ {OV5693_8BIT, 0x3808, 0x07},
+ {OV5693_8BIT, 0x3809, 0x94},
+ {OV5693_8BIT, 0x380a, 0x04},
+ {OV5693_8BIT, 0x380b, 0x48},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x02},
+ {OV5693_8BIT, 0x3813, 0x02},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x80},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_2592x1456_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa4},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x20},
+ {OV5693_8BIT, 0x380a, 0x05},
+ {OV5693_8BIT, 0x380b, 0xb0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+static struct ov5693_reg const ov5693_2576x1456_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00},
+ {OV5693_8BIT, 0x3801, 0x00},
+ {OV5693_8BIT, 0x3802, 0x00},
+ {OV5693_8BIT, 0x3803, 0xf0},
+ {OV5693_8BIT, 0x3804, 0x0a},
+ {OV5693_8BIT, 0x3805, 0x3f},
+ {OV5693_8BIT, 0x3806, 0x06},
+ {OV5693_8BIT, 0x3807, 0xa4},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x05},
+ {OV5693_8BIT, 0x380b, 0xb0},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x18},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+/*
+ * 2592x1944 30fps 0.6ms VBlanking 2lane 10Bit
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_2592x1944_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x20},
+ {OV5693_8BIT, 0x380a, 0x07},
+ {OV5693_8BIT, 0x380b, 0x98},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x10},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+/*
+ * 11:9 Full FOV Output, expected FOV Res: 2346x1920
+ * ISP Effect Res: 1408x1152
+ * Sensor out: 1424x1168, DS From: 2380x1952
+ *
+ * WA: Left Offset: 8, Hor scal: 64
+ */
+#if ENABLE_NON_PREVIEW
+static struct ov5693_reg const ov5693_1424x1168_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x3b}, /* long exposure[15:8] */
+ {OV5693_8BIT, 0x3502, 0x80}, /* long exposure[7:0] */
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00}, /* TIMING_X_ADDR_START */
+ {OV5693_8BIT, 0x3801, 0x50}, /* 80 */
+ {OV5693_8BIT, 0x3802, 0x00}, /* TIMING_Y_ADDR_START */
+ {OV5693_8BIT, 0x3803, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3804, 0x09}, /* TIMING_X_ADDR_END */
+ {OV5693_8BIT, 0x3805, 0xdd}, /* 2525 */
+ {OV5693_8BIT, 0x3806, 0x07}, /* TIMING_Y_ADDR_END */
+ {OV5693_8BIT, 0x3807, 0xa1}, /* 1953 */
+ {OV5693_8BIT, 0x3808, 0x05}, /* TIMING_X_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x3809, 0x90}, /* 1424 */
+ {OV5693_8BIT, 0x380a, 0x04}, /* TIMING_Y_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x380b, 0x90}, /* 1168 */
+ {OV5693_8BIT, 0x380c, 0x0a}, /* TIMING_HTS */
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /* TIMING_VTS */
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3810, 0x00}, /* TIMING_ISP_X_WIN */
+ {OV5693_8BIT, 0x3811, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3812, 0x00}, /* TIMING_ISP_Y_WIN */
+ {OV5693_8BIT, 0x3813, 0x00}, /* 0 */
+ {OV5693_8BIT, 0x3814, 0x11}, /* TIME_X_INC */
+ {OV5693_8BIT, 0x3815, 0x11}, /* TIME_Y_INC */
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+#endif
+
+/*
+ * 3:2 Full FOV Output, expected FOV Res: 2560x1706
+ * ISP Effect Res: 720x480
+ * Sensor out: 736x496, DS From 2616x1764
+ */
+static struct ov5693_reg const ov5693_736x496_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x3b}, /* long exposure[15:8] */
+ {OV5693_8BIT, 0x3502, 0x80}, /* long exposure[7:0] */
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3800, 0x00}, /* TIMING_X_ADDR_START */
+ {OV5693_8BIT, 0x3801, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3802, 0x00}, /* TIMING_Y_ADDR_START */
+ {OV5693_8BIT, 0x3803, 0x62}, /* 98 */
+ {OV5693_8BIT, 0x3804, 0x0a}, /* TIMING_X_ADDR_END */
+ {OV5693_8BIT, 0x3805, 0x3b}, /* 2619 */
+ {OV5693_8BIT, 0x3806, 0x07}, /* TIMING_Y_ADDR_END */
+ {OV5693_8BIT, 0x3807, 0x43}, /* 1859 */
+ {OV5693_8BIT, 0x3808, 0x02}, /* TIMING_X_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x3809, 0xe0}, /* 736 */
+ {OV5693_8BIT, 0x380a, 0x01}, /* TIMING_Y_OUTPUT_SIZE */
+ {OV5693_8BIT, 0x380b, 0xf0}, /* 496 */
+ {OV5693_8BIT, 0x380c, 0x0a}, /* TIMING_HTS */
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07}, /* TIMING_VTS */
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3810, 0x00}, /* TIMING_ISP_X_WIN */
+ {OV5693_8BIT, 0x3811, 0x02}, /* 2 */
+ {OV5693_8BIT, 0x3812, 0x00}, /* TIMING_ISP_Y_WIN */
+ {OV5693_8BIT, 0x3813, 0x00}, /* 0 */
+ {OV5693_8BIT, 0x3814, 0x11}, /* TIME_X_INC */
+ {OV5693_8BIT, 0x3815, 0x11}, /* TIME_Y_INC */
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_reg const ov5693_2576x1936_30fps[] = {
+ {OV5693_8BIT, 0x3501, 0x7b},
+ {OV5693_8BIT, 0x3502, 0x00},
+ {OV5693_8BIT, 0x3708, 0xe2},
+ {OV5693_8BIT, 0x3709, 0xc3},
+ {OV5693_8BIT, 0x3803, 0x00},
+ {OV5693_8BIT, 0x3806, 0x07},
+ {OV5693_8BIT, 0x3807, 0xa3},
+ {OV5693_8BIT, 0x3808, 0x0a},
+ {OV5693_8BIT, 0x3809, 0x10},
+ {OV5693_8BIT, 0x380a, 0x07},
+ {OV5693_8BIT, 0x380b, 0x90},
+ {OV5693_8BIT, 0x380c, 0x0a},
+ {OV5693_8BIT, 0x380d, 0x80},
+ {OV5693_8BIT, 0x380e, 0x07},
+ {OV5693_8BIT, 0x380f, 0xc0},
+ {OV5693_8BIT, 0x3811, 0x18},
+ {OV5693_8BIT, 0x3813, 0x00},
+ {OV5693_8BIT, 0x3814, 0x11},
+ {OV5693_8BIT, 0x3815, 0x11},
+ {OV5693_8BIT, 0x3820, 0x00},
+ {OV5693_8BIT, 0x3821, 0x1e},
+ {OV5693_8BIT, 0x5002, 0x00},
+ {OV5693_8BIT, 0x0100, 0x01},
+ {OV5693_TOK_TERM, 0, 0}
+};
+
+static struct ov5693_resolution ov5693_res_preview[] = {
+ {
+ .desc = "ov5693_736x496_30fps",
+ .width = 736,
+ .height = 496,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_736x496_30fps,
+ },
+ {
+ .desc = "ov5693_1616x1216_30fps",
+ .width = 1616,
+ .height = 1216,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1616x1216_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2576,
+ .height = 1456,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2576x1456_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2576,
+ .height = 1936,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2576x1936_30fps,
+ },
+};
+
+#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview))
+
+/*
+ * Disable non-preview configurations until the configuration selection is
+ * improved.
+ */
+#if ENABLE_NON_PREVIEW
+struct ov5693_resolution ov5693_res_still[] = {
+ {
+ .desc = "ov5693_736x496_30fps",
+ .width = 736,
+ .height = 496,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_736x496_30fps,
+ },
+ {
+ .desc = "ov5693_1424x1168_30fps",
+ .width = 1424,
+ .height = 1168,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1424x1168_30fps,
+ },
+ {
+ .desc = "ov5693_1616x1216_30fps",
+ .width = 1616,
+ .height = 1216,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1616x1216_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1456,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1456_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1944,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1944_30fps,
+ },
+};
+
+#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still))
+
+struct ov5693_resolution ov5693_res_video[] = {
+ {
+ .desc = "ov5693_736x496_30fps",
+ .width = 736,
+ .height = 496,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_736x496,
+ },
+ {
+ .desc = "ov5693_336x256_30fps",
+ .width = 336,
+ .height = 256,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_336x256,
+ },
+ {
+ .desc = "ov5693_368x304_30fps",
+ .width = 368,
+ .height = 304,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_368x304,
+ },
+ {
+ .desc = "ov5693_192x160_30fps",
+ .width = 192,
+ .height = 160,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 1,
+ .regs = ov5693_192x160,
+ },
+ {
+ .desc = "ov5693_1296x736_30fps",
+ .width = 1296,
+ .height = 736,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 0,
+ .regs = ov5693_1296x736,
+ },
+ {
+ .desc = "ov5693_1296x976_30fps",
+ .width = 1296,
+ .height = 976,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 2,
+ .bin_factor_y = 2,
+ .bin_mode = 0,
+ .regs = ov5693_1296x976,
+ },
+ {
+ .desc = "ov5693_1636P_30fps",
+ .width = 1636,
+ .height = 1096,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1636p_30fps,
+ },
+ {
+ .desc = "ov5693_1080P_30fps",
+ .width = 1940,
+ .height = 1096,
+ .fps = 30,
+ .pix_clk_freq = 160,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_1940x1096,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1456,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1456_30fps,
+ },
+ {
+ .desc = "ov5693_5M_30fps",
+ .width = 2592,
+ .height = 1944,
+ .pix_clk_freq = 160,
+ .fps = 30,
+ .used = 0,
+ .pixels_per_line = 2688,
+ .lines_per_frame = 1984,
+ .bin_factor_x = 1,
+ .bin_factor_y = 1,
+ .bin_mode = 0,
+ .regs = ov5693_2592x1944_30fps,
+ .crop = {
+ .left = 0,
+ .top = 0,
+ .width = 2592,
+ .height = 1944
+ },
+ },
+};
+
+#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video))
+#endif
+
+static struct ov5693_resolution *ov5693_res = ov5693_res_video;
+static unsigned long N_RES = N_RES_VIDEO;
+#endif
--
2.31.1
From 488d44e707524867a901a4f8c9ba85b0c21f23af Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 17 Jan 2021 19:08:18 +0000
Subject: [PATCH] media: i2c: Add reset pin toggling to ov5693
The ov5693 has an xshutdown pin which can be present and, if so, needs
toggling as part of power on sequence.
Add calls to handle the reset GPIO
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 32485e4ed42b..f9ced52ad37a 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1085,6 +1085,8 @@ static int __power_up(struct v4l2_subdev *sd)
if (ret)
goto fail_power;
+ gpiod_set_value_cansleep(sensor->reset, 0);
+
__cci_delay(up_delay);
return 0;
@@ -1103,6 +1105,8 @@ static int power_down(struct v4l2_subdev *sd)
dev->focus = OV5693_INVALID_CONFIG;
+ gpiod_set_value_cansleep(sensor->reset, 1);
+
clk_disable_unprepare(dev->clk);
if (dev->indicator_led)
--
2.31.1
From dc022a7cb6da36033151cabbd014930083b8ab99 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 17 Jan 2021 21:39:15 +0000
Subject: [PATCH] media: i2c: Fix misnamed variable in power_down() for ov5693
Fix the misnamed variable in gpiod_set_value_cansleep().
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 f9ced52ad37a..9fd44a3d1d85 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1105,7 +1105,7 @@ static int power_down(struct v4l2_subdev *sd)
dev->focus = OV5693_INVALID_CONFIG;
- gpiod_set_value_cansleep(sensor->reset, 1);
+ gpiod_set_value_cansleep(dev->reset, 1);
clk_disable_unprepare(dev->clk);
--
2.31.1
From bfe339c0170e1ef63fd69a4651e8ead0fac22f74 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 143f3c0f445e..806d4e5fc177 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].node = &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) {
@@ -193,11 +224,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);
@@ -205,7 +240,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]);
if (!fwnode) {
@@ -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);
err_out:
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.31.1
From f7b131d6c1f8813f327ca9eeea2639641d238ba7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabian=20W=C3=BCthrich?= <me@fabwu.ch>
Date: Fri, 22 Jan 2021 21:23:47 +0100
Subject: [PATCH] ov5693: Add orientation and rotation controls
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Parse orientation and rotation from fwnodes and initialize the
respective controls.
Signed-off-by: Fabian Wüthrich <me@fabwu.ch>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 9fd44a3d1d85..1a85800df7ed 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -31,6 +31,7 @@
#include <linux/i2c.h>
#include <linux/moduleparam.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
#include <linux/io.h>
#include <linux/acpi.h>
#include <linux/regulator/consumer.h>
@@ -1608,6 +1609,7 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov5693->sd);
const struct v4l2_ctrl_ops *ops = &ov5693_ctrl_ops;
+ struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl *ctrl;
unsigned int i;
int ret;
@@ -1663,6 +1665,15 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
if (ov5693->hblank)
ov5693->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ /* set properties from fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ return ret;
+
+ ret = v4l2_ctrl_new_fwnode_properties(&ov5693->ctrl_handler, ops, &props);
+ if (ret)
+ return ret;
+
/* Use same lock for controls as for everything else. */
ov5693->ctrl_handler.lock = &ov5693->input_lock;
ov5693->sd.ctrl_handler = &ov5693->ctrl_handler;
--
2.31.1
From 1e9b82baddeff537b88a9a3d607dea6cd98da5c8 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 23 Jan 2021 00:28:32 +0000
Subject: [PATCH] platform: x86: Stylistic updates for intel-skl-int3472
This commit makes a bunch of stylistic updates, minor changes and other
stuff that's part of the improvements pass I'm doing to the code after
taking into account feedback from the list.
It also alters the ACPI buffer fetching code to be more generalisable so
I can re-use it to fetch the clock frequency.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
.../platform/x86/intel_skl_int3472_common.c | 37 ++++---
.../platform/x86/intel_skl_int3472_common.h | 7 +-
.../platform/x86/intel_skl_int3472_discrete.c | 101 +++++++++---------
.../platform/x86/intel_skl_int3472_tps68470.c | 16 +--
4 files changed, 89 insertions(+), 72 deletions(-)
diff --git a/drivers/platform/x86/intel_skl_int3472_common.c b/drivers/platform/x86/intel_skl_int3472_common.c
index 08cb9d3c06aa..549d211979e1 100644
--- a/drivers/platform/x86/intel_skl_int3472_common.c
+++ b/drivers/platform/x86/intel_skl_int3472_common.c
@@ -7,41 +7,52 @@
#include "intel_skl_int3472_common.h"
-int skl_int3472_get_cldb_buffer(struct acpi_device *adev,
- struct int3472_cldb *cldb)
+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;
- int ret = 0;
- status = acpi_evaluate_object(handle, "CLDB", NULL, &buffer);
+ status = acpi_evaluate_object(handle, id, NULL, &buffer);
if (ACPI_FAILURE(status))
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
obj = buffer.pointer;
if (!obj) {
- dev_err(&adev->dev, "ACPI device has no CLDB object\n");
- return -ENODEV;
+ dev_err(&adev->dev, "ACPI device has no %s object\n", id);
+ return ERR_PTR(-ENODEV);
}
if (obj->type != ACPI_TYPE_BUFFER) {
- dev_err(&adev->dev, "CLDB object is not an ACPI buffer\n");
- ret = -EINVAL;
- goto out_free_buff;
+ dev_err(&adev->dev, "%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 = 0;
+
+ obj = skl_int3472_get_acpi_buffer(adev, "CLDB");
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
if (obj->buffer.length > sizeof(*cldb)) {
dev_err(&adev->dev, "The CLDB buffer is too large\n");
ret = -EINVAL;
- goto out_free_buff;
+ goto out_free_obj;
}
memcpy(cldb, obj->buffer.pointer, obj->buffer.length);
-out_free_buff:
- kfree(buffer.pointer);
+out_free_obj:
+ kfree(obj);
return ret;
}
diff --git a/drivers/platform/x86/intel_skl_int3472_common.h b/drivers/platform/x86/intel_skl_int3472_common.h
index 4ac6bb2b223f..e1083bb67dc6 100644
--- a/drivers/platform/x86/intel_skl_int3472_common.h
+++ b/drivers/platform/x86/intel_skl_int3472_common.h
@@ -29,7 +29,7 @@
#define INT3472_GPIO_FUNCTION_REMAP(_PIN, _FUNCTION) \
(const struct int3472_gpio_function_remap) { \
- .documented = _PIN, \
+ .documented = _PIN, \
.actual = _FUNCTION \
}
@@ -95,5 +95,6 @@ struct int3472_sensor_config {
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);
-int skl_int3472_get_cldb_buffer(struct acpi_device *adev,
- struct int3472_cldb *cldb);
+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);
diff --git a/drivers/platform/x86/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel_skl_int3472_discrete.c
index ea7e57f3e3f0..42ae8396eb64 100644
--- a/drivers/platform/x86/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel_skl_int3472_discrete.c
@@ -12,12 +12,12 @@
#include "intel_skl_int3472_common.h"
-/* 79234640-9e10-4fea-a5c1b5aa8b19756f */
+/* 79234640-9e10-4fea-a5c1-b5aa8b19756f */
static const guid_t int3472_gpio_guid =
GUID_INIT(0x79234640, 0x9e10, 0x4fea,
0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
-/* 822ace8f-2814-4174-a56b5f029fe079ee */
+/* 822ace8f-2814-4174-a56b-5f029fe079ee */
static const guid_t cio2_sensor_module_guid =
GUID_INIT(0x822ace8f, 0x2814, 0x4174,
0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
@@ -94,7 +94,7 @@ static const struct clk_ops skl_int3472_clock_ops = {
};
static struct int3472_sensor_config *
-int3472_get_sensor_module_config(struct int3472_device *int3472)
+skl_int3472_get_sensor_module_config(struct int3472_device *int3472)
{
unsigned int i = ARRAY_SIZE(int3472_sensor_configs);
struct int3472_sensor_config *ret;
@@ -131,9 +131,9 @@ int3472_get_sensor_module_config(struct int3472_device *int3472)
return ret;
}
-static int int3472_map_gpio_to_sensor(struct int3472_device *int3472,
- struct acpi_resource *ares,
- char *func, u32 polarity)
+static int skl_int3472_map_gpio_to_sensor(struct int3472_device *int3472,
+ struct acpi_resource *ares,
+ char *func, u32 polarity)
{
char *path = ares->data.gpio.resource_source.string_ptr;
struct int3472_sensor_config *sensor_config;
@@ -143,7 +143,7 @@ static int int3472_map_gpio_to_sensor(struct int3472_device *int3472,
acpi_status status;
int ret;
- sensor_config = int3472_get_sensor_module_config(int3472);
+ sensor_config = skl_int3472_get_sensor_module_config(int3472);
if (!IS_ERR(sensor_config) && sensor_config->function_maps) {
unsigned int i = 0;
@@ -186,17 +186,19 @@ static int int3472_map_gpio_to_sensor(struct int3472_device *int3472,
return 0;
}
-static int int3472_register_clock(struct int3472_device *int3472,
- struct acpi_resource *ares)
+static int skl_int3472_register_clock(struct int3472_device *int3472,
+ struct acpi_resource *ares)
{
char *path = ares->data.gpio.resource_source.string_ptr;
- struct clk_init_data init = { };
+ struct clk_init_data init = { 0 };
int ret = 0;
- init.name = kasprintf(GFP_KERNEL, "%s-clk", acpi_dev_name(int3472->adev));
+ init.name = kasprintf(GFP_KERNEL, "%s-clk",
+ acpi_dev_name(int3472->adev));
init.ops = &skl_int3472_clock_ops;
- int3472->clock.gpio = acpi_get_gpiod(path, ares->data.gpio.pin_table[0]);
+ int3472->clock.gpio = acpi_get_gpiod(path,
+ ares->data.gpio.pin_table[0]);
if (IS_ERR(int3472->clock.gpio)) {
ret = PTR_ERR(int3472->clock.gpio);
goto out_free_init_name;
@@ -226,17 +228,16 @@ static int int3472_register_clock(struct int3472_device *int3472,
return ret;
}
-static int int3472_register_regulator(struct int3472_device *int3472,
- struct acpi_resource *ares)
+static int skl_int3472_register_regulator(struct int3472_device *int3472,
+ struct acpi_resource *ares)
{
char *path = ares->data.gpio.resource_source.string_ptr;
struct int3472_sensor_config *sensor_config;
struct regulator_init_data init_data = { };
- struct int3472_gpio_regulator *regulator;
struct regulator_config cfg = { };
int ret;
- sensor_config = int3472_get_sensor_module_config(int3472);
+ sensor_config = skl_int3472_get_sensor_module_config(int3472);
if (IS_ERR_OR_NULL(sensor_config)) {
dev_err(&int3472->pdev->dev, "No sensor module config\n");
return PTR_ERR(sensor_config);
@@ -252,26 +253,29 @@ static int int3472_register_regulator(struct int3472_device *int3472,
init_data.num_consumer_supplies = 1;
init_data.consumer_supplies = &sensor_config->supply_map;
- snprintf(int3472->regulator.regulator_name, GPIO_REGULATOR_NAME_LENGTH,
- "int3472-discrete-regulator");
- snprintf(int3472->regulator.supply_name, GPIO_REGULATOR_SUPPLY_NAME_LENGTH,
- "supply-0");
+ snprintf(int3472->regulator.regulator_name,
+ GPIO_REGULATOR_NAME_LENGTH, "int3472-discrete-regulator");
+ 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.rdesc = INT3472_REGULATOR(
+ int3472->regulator.regulator_name,
+ int3472->regulator.supply_name,
+ &int3472_gpio_regulator_ops);
- int3472->regulator.gpio = acpi_get_gpiod(path, ares->data.gpio.pin_table[0]);
+ int3472->regulator.gpio = acpi_get_gpiod(path,
+ ares->data.gpio.pin_table[0]);
if (IS_ERR(int3472->regulator.gpio)) {
- ret = PTR_ERR(int3472->regulator.gpio);
- goto err_free_regulator;
+ dev_err(&int3472->pdev->dev, "Failed to get 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);
+ 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;
@@ -280,15 +284,13 @@ static int int3472_register_regulator(struct int3472_device *int3472,
return 0;
err_free_gpio:
- gpiod_put(regulator->gpio);
-err_free_regulator:
- kfree(regulator);
+ gpiod_put(int3472->regulator.gpio);
return ret;
}
/**
- * int3472_handle_gpio_resources: maps PMIC resources to consuming sensor
+ * skl_int3472_handle_gpio_resources: maps PMIC resources to consuming sensor
* @ares: A pointer to a &struct acpi_resource
* @data: A pointer to a &struct int3472_device
*
@@ -305,8 +307,9 @@ static int int3472_register_regulator(struct int3472_device *int3472,
*
* 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. These will be handled by the
- * mapping sub-functions.
+ * 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.
@@ -317,8 +320,8 @@ static int int3472_register_regulator(struct int3472_device *int3472,
* * -ENODEV - If the resource has no corresponding _DSM entry
* * -Other - Errors propagated from one of the sub-functions.
*/
-static int int3472_handle_gpio_resources(struct acpi_resource *ares,
- void *data)
+static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
+ void *data)
{
struct int3472_device *int3472 = data;
union acpi_object *obj;
@@ -345,30 +348,30 @@ static int int3472_handle_gpio_resources(struct acpi_resource *ares,
switch (obj->integer.value & 0xff) {
case INT3472_GPIO_TYPE_RESET:
- ret = int3472_map_gpio_to_sensor(int3472, ares, "reset",
- GPIO_ACTIVE_LOW);
+ ret = skl_int3472_map_gpio_to_sensor(int3472, ares, "reset",
+ GPIO_ACTIVE_LOW);
if (ret)
dev_err(&int3472->pdev->dev,
"Failed to map reset pin to sensor\n");
break;
case INT3472_GPIO_TYPE_POWERDOWN:
- ret = int3472_map_gpio_to_sensor(int3472, ares, "powerdown",
- GPIO_ACTIVE_LOW);
+ ret = skl_int3472_map_gpio_to_sensor(int3472, ares, "powerdown",
+ GPIO_ACTIVE_LOW);
if (ret)
dev_err(&int3472->pdev->dev,
"Failed to map powerdown pin to sensor\n");
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
- ret = int3472_register_clock(int3472, ares);
+ ret = skl_int3472_register_clock(int3472, ares);
if (ret)
dev_err(&int3472->pdev->dev,
"Failed to map clock to sensor\n");
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
- ret = int3472_register_regulator(int3472, ares);
+ ret = skl_int3472_register_regulator(int3472, ares);
if (ret) {
dev_err(&int3472->pdev->dev,
"Failed to map regulator to sensor\n");
@@ -376,8 +379,9 @@ static int int3472_handle_gpio_resources(struct acpi_resource *ares,
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
- ret = int3472_map_gpio_to_sensor(int3472, ares, "indicator-led",
- GPIO_ACTIVE_HIGH);
+ ret = skl_int3472_map_gpio_to_sensor(int3472, ares,
+ "indicator-led",
+ GPIO_ACTIVE_HIGH);
if (ret)
dev_err(&int3472->pdev->dev,
"Failed to map indicator led to sensor\n");
@@ -396,7 +400,7 @@ static int int3472_handle_gpio_resources(struct acpi_resource *ares,
return ret;
}
-static int int3472_parse_crs(struct int3472_device *int3472)
+static int skl_int3472_parse_crs(struct int3472_device *int3472)
{
struct list_head resource_list;
int ret = 0;
@@ -404,7 +408,8 @@ static int int3472_parse_crs(struct int3472_device *int3472)
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(int3472->adev, &resource_list,
- int3472_handle_gpio_resources, int3472);
+ skl_int3472_handle_gpio_resources,
+ int3472);
if (!ret) {
gpiod_add_lookup_table(&int3472->gpios);
@@ -423,7 +428,7 @@ int skl_int3472_discrete_probe(struct platform_device *pdev)
struct int3472_cldb cldb;
int ret = 0;
- ret = skl_int3472_get_cldb_buffer(adev, &cldb);
+ ret = skl_int3472_fill_cldb(adev, &cldb);
if (ret || cldb.control_logic_type != 1)
return -EINVAL;
@@ -444,10 +449,10 @@ int skl_int3472_discrete_probe(struct platform_device *pdev)
ret = -ENODEV;
goto err_free_int3472;
}
- int3472->sensor_name = i2c_acpi_dev_name(int3472->sensor);
+ int3472->sensor_name = kasprintf(GFP_KERNEL, I2C_DEV_NAME_FORMAT, acpi_dev_name(int3472->sensor));
int3472->gpios.dev_id = int3472->sensor_name;
- ret = int3472_parse_crs(int3472);
+ ret = skl_int3472_parse_crs(int3472);
if (ret) {
skl_int3472_discrete_remove(pdev);
goto err_return_ret;
diff --git a/drivers/platform/x86/intel_skl_int3472_tps68470.c b/drivers/platform/x86/intel_skl_int3472_tps68470.c
index 3fe27ec0caff..40629291b339 100644
--- a/drivers/platform/x86/intel_skl_int3472_tps68470.c
+++ b/drivers/platform/x86/intel_skl_int3472_tps68470.c
@@ -87,20 +87,20 @@ int skl_int3472_tps68470_probe(struct i2c_client *client)
/*
* 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:
+ * 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.
+ * 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.
+ * 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; crash and burn.
*/
- ret = skl_int3472_get_cldb_buffer(adev, &cldb);
+ ret = skl_int3472_fill_cldb(adev, &cldb);
if (!ret && cldb.control_logic_type != 2)
return -EINVAL;
--
2.31.1
From 3b0e86111cd5d4f0478c8d7aa45d43821c00c3b3 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 23 Jan 2021 00:30:15 +0000
Subject: [PATCH] platform: x86: Add recalc_rate opp to int3472-discrete clock
This commit adds the recalc_rate opp to the clock registered by
int3472-discrete so that sensor drivers calling clk_get_rate() will get a
valid value returned.
The value is simply read from the sensor's SSDB buffer, and so we pass
CLK_GET_RATE_NOCACHE
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
.../platform/x86/intel_skl_int3472_common.h | 6 +++
.../platform/x86/intel_skl_int3472_discrete.c | 37 ++++++++++++++++++-
2 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/intel_skl_int3472_common.h b/drivers/platform/x86/intel_skl_int3472_common.h
index e1083bb67dc6..860c849b7769 100644
--- a/drivers/platform/x86/intel_skl_int3472_common.h
+++ b/drivers/platform/x86/intel_skl_int3472_common.h
@@ -17,6 +17,8 @@
#define GPIO_REGULATOR_NAME_LENGTH 27
#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, \
@@ -36,6 +38,9 @@
#define to_int3472_clk(hw) \
container_of(hw, struct int3472_gpio_clock, clk_hw)
+#define to_int3472_device(clk) \
+ container_of(clk, struct int3472_device, clock)
+
struct int3472_cldb {
u8 version;
/*
@@ -62,6 +67,7 @@ struct int3472_gpio_regulator {
struct int3472_gpio_clock {
struct clk *clk;
struct clk_hw clk_hw;
+ struct clk_lookup *cl;
struct gpio_desc *gpio;
};
diff --git a/drivers/platform/x86/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel_skl_int3472_discrete.c
index 42ae8396eb64..98eb1ec3399e 100644
--- a/drivers/platform/x86/intel_skl_int3472_discrete.c
+++ b/drivers/platform/x86/intel_skl_int3472_discrete.c
@@ -86,11 +86,41 @@ static void skl_int3472_clk_unprepare(struct clk_hw *hw)
/* Likewise, nothing to do here... */
}
+static unsigned int skl_int3472_get_clk_frequency(struct int3472_device *int3472)
+{
+ union acpi_object *obj;
+ unsigned int ret = 0;
+
+ obj = skl_int3472_get_acpi_buffer(int3472->sensor, "SSDB");
+ if (IS_ERR(obj))
+ goto out_free_buff; /* report rate as 0 on error */
+
+ if (obj->buffer.length < CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET + sizeof(u32)) {
+ dev_err(&int3472->pdev->dev, "The buffer is too small\n");
+ goto out_free_buff;
+ }
+
+ ret = *(u32*)(obj->buffer.pointer + CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET);
+
+out_free_buff:
+ kfree(obj);
+ return ret;
+}
+
+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);
+ struct int3472_device *int3472 = to_int3472_device(clk);
+
+ return skl_int3472_get_clk_frequency(int3472);
+}
+
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,
};
static struct int3472_sensor_config *
@@ -196,6 +226,7 @@ static int skl_int3472_register_clock(struct int3472_device *int3472,
init.name = kasprintf(GFP_KERNEL, "%s-clk",
acpi_dev_name(int3472->adev));
init.ops = &skl_int3472_clock_ops;
+ init.flags |= CLK_GET_RATE_NOCACHE;
int3472->clock.gpio = acpi_get_gpiod(path,
ares->data.gpio.pin_table[0]);
@@ -212,8 +243,9 @@ static int skl_int3472_register_clock(struct int3472_device *int3472,
goto err_put_gpio;
}
- ret = clk_register_clkdev(int3472->clock.clk, "xvclk", int3472->sensor_name);
- if (ret)
+ int3472->clock.cl = clkdev_create(int3472->clock.clk, "xvclk",
+ int3472->sensor_name);
+ if (IS_ERR_OR_NULL(int3472->clock.cl))
goto err_unregister_clk;
goto out_free_init_name;
@@ -483,6 +515,7 @@ int skl_int3472_discrete_remove(struct platform_device *pdev)
if (!IS_ERR_OR_NULL(int3472->clock.clk)) {
gpiod_put(int3472->clock.gpio);
clk_unregister(int3472->clock.clk);
+ clkdev_drop(int3472->clock.cl);
}
acpi_dev_put(int3472->sensor);
--
2.31.1
From b61bdcf5ebe19d2d8781074fdb3d3f2a7c247109 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 806d4e5fc177..3c373ad1c0b0 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.31.1
From 8355535afa261a7599c589440f71d2b06b77b168 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 8 Feb 2021 21:44:38 +0000
Subject: [PATCH] media: i2c: Tidy up ov5693_init_controls()
The ov5693 driver initialises a bunch of v4l2 controls and throws away
the pointers. This seems weird, let's not do that.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 46 ++++++++++++++++++++++----------------
drivers/media/i2c/ov5693.h | 12 +++++++++-
2 files changed, 38 insertions(+), 20 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 1a85800df7ed..a9747ab783d7 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1610,7 +1610,6 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
struct i2c_client *client = v4l2_get_subdevdata(&ov5693->sd);
const struct v4l2_ctrl_ops *ops = &ov5693_ctrl_ops;
struct v4l2_fwnode_device_properties props;
- struct v4l2_ctrl *ctrl;
unsigned int i;
int ret;
int hblank;
@@ -1628,15 +1627,17 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
NULL);
/* link freq */
- ctrl = v4l2_ctrl_new_int_menu(&ov5693->ctrl_handler, NULL,
- V4L2_CID_LINK_FREQ,
- 0, 0, link_freq_menu_items);
- if (ctrl)
- ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ ov5693->ctrls.link_freq = v4l2_ctrl_new_int_menu(&ov5693->ctrl_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 */
- v4l2_ctrl_new_std(&ov5693->ctrl_handler, NULL, V4L2_CID_PIXEL_RATE,
- 0, OV5693_PIXEL_RATE, 1, OV5693_PIXEL_RATE);
+ ov5693->ctrls.pixel_rate = v4l2_ctrl_new_std(&ov5693->ctrl_handler, NULL,
+ V4L2_CID_PIXEL_RATE, 0,
+ OV5693_PIXEL_RATE, 1,
+ OV5693_PIXEL_RATE);
if (ov5693->ctrl_handler.error) {
ov5693_remove(client);
@@ -1645,25 +1646,32 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
/* Exposure */
- v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16,
- 512);
+ ov5693->ctrls.exposure = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_EXPOSURE, 16,
+ 1048575, 16, 512);
/* Gain */
- v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_ANALOGUE_GAIN, 1, 1023, 1, 128);
- v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_DIGITAL_GAIN, 1, 3999, 1, 1000);
+ ov5693->ctrls.analogue_gain = v4l2_ctrl_new_std(&ov5693->ctrl_handler,
+ ops, V4L2_CID_ANALOGUE_GAIN,
+ 1, 1023, 1, 128);
+ ov5693->ctrls.digital_gain = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_DIGITAL_GAIN, 1,
+ 3999, 1, 1000);
/* Flip */
- v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ov5693->ctrls.hflip = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ov5693->ctrls.vflip = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
hblank = OV5693_PPL_DEFAULT - ov5693->mode->width;
- ov5693->hblank = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
- V4L2_CID_HBLANK, hblank, hblank,
- 1, hblank);
- if (ov5693->hblank)
- ov5693->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ ov5693->ctrls.hblank = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_HBLANK, hblank, hblank,
+ 1, hblank);
+ if (ov5693->ctrls.hblank)
+ ov5693->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
/* set properties from fwnode (e.g. rotation, orientation) */
ret = v4l2_fwnode_device_parse(&client->dev, &props);
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 9a508e1f3624..26819cf3f4d2 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -270,7 +270,17 @@ struct ov5693_device {
bool has_vcm;
- struct v4l2_ctrl *hblank;
+ struct ov5693_v4l2_ctrls {
+ 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;
+ } ctrls;
+
};
enum ov5693_tok_type {
--
2.31.1
From 5810385a943520d0c350e453a34fbe1f06eeadfc Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 8 Feb 2021 21:46:49 +0000
Subject: [PATCH] media: i2c: Remove OV5693_PPL_DEFAULT
No need for this macro, the PPL setting is against the mode structs.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index a9747ab783d7..7fb368eec327 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -105,8 +105,6 @@ MODULE_PARM_DESC(up_delay,
#define OV5693_PIXEL_ARRAY_WIDTH 2592U
#define OV5693_PIXEL_ARRAY_HEIGHT 1944U
-#define OV5693_PPL_DEFAULT 2800
-
static int vcm_ad_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
{
int err;
@@ -1666,7 +1664,7 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
ov5693->ctrls.vflip = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
- hblank = OV5693_PPL_DEFAULT - ov5693->mode->width;
+ hblank = ov5693->mode->pixels_per_line - ov5693->mode->width;
ov5693->ctrls.hblank = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
V4L2_CID_HBLANK, hblank, hblank,
1, hblank);
--
2.31.1
From 17cb27cf93f98b262a0e0efd25f91d35c49be2f7 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 8 Feb 2021 22:53:02 +0000
Subject: [PATCH] media: i2c: Add vblank control to ov5693 driver
The latest libcamera requires a V4L2_CID_VBLANK control in each sensor
driver; add a skeleton one to the ov5693 to fulfill the requirement.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 12 ++++++++++++
drivers/media/i2c/ov5693.h | 3 +++
2 files changed, 15 insertions(+)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 7fb368eec327..1950d7ac2d54 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -946,6 +946,10 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
return ov5693_flip_horz_configure(dev, !!ctrl->val);
case V4L2_CID_VFLIP:
return ov5693_flip_vert_configure(dev, !!ctrl->val);
+ case V4L2_CID_VBLANK:
+ ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_TIMING_VTS_H,
+ dev->mode->height + ctrl->val);
+ break;
default:
ret = -EINVAL;
}
@@ -1611,6 +1615,7 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
unsigned int i;
int ret;
int hblank;
+ int vblank_max, vblank_min, vblank_def;
ret = v4l2_ctrl_handler_init(&ov5693->ctrl_handler,
ARRAY_SIZE(ov5693_controls));
@@ -1671,6 +1676,13 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
if (ov5693->ctrls.hblank)
ov5693->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ vblank_max = OV5693_TIMING_MAX_VTS - ov5693->mode->height;
+ vblank_def = ov5693->mode->lines_per_frame - ov5693->mode->height;
+ vblank_min = ov5693->mode->lines_per_frame - ov5693->mode->height;
+ ov5693->ctrls.vblank = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
+ V4L2_CID_VBLANK, vblank_min,
+ vblank_max, 1, vblank_def);
+
/* set properties from fwnode (e.g. rotation, orientation) */
ret = v4l2_fwnode_device_parse(&client->dev, &props);
if (ret)
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 26819cf3f4d2..9d7eed97963b 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -131,6 +131,8 @@
/*High 8-bit, and low 8-bit HTS address is 0x380f*/
#define OV5693_TIMING_VTS_L 0x380f
+#define OV5693_TIMING_MAX_VTS 0xffff
+
#define OV5693_MWB_RED_GAIN_H 0x3400
#define OV5693_MWB_GREEN_GAIN_H 0x3402
#define OV5693_MWB_BLUE_GAIN_H 0x3404
@@ -279,6 +281,7 @@ struct ov5693_device {
struct v4l2_ctrl *hflip;
struct v4l2_ctrl *vflip;
struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
} ctrls;
};
--
2.31.1
From 8f399550aace680ac9e8391ce1438565f4ee3e2d Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 10 Feb 2021 00:36:32 +0000
Subject: [PATCH] media: i2c: update exposure control for ov5693
The exposure control for ov5693 currently is in units of 1/16th of a line,
but I think the framework expects it in units of lines. Set the control to
work in lines and simply apply the multiplication when configuring the chip
registers instead.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 1950d7ac2d54..cea767230aa9 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -801,6 +801,12 @@ static int ov5693_exposure_configure(struct ov5693_device *sensor, u32 exposure)
{
int ret;
+ /*
+ * The control for exposure seems to be in units of lines, but the chip
+ * datasheet specifies exposure is in units of 1/16th of a line.
+ */
+ exposure = exposure * 16;
+
ov5693_get_exposure(sensor);
ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
OV5693_EXPOSURE_CTRL_HH_REG, OV5693_EXPOSURE_CTRL_HH(exposure));
@@ -910,6 +916,16 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
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 = dev->mode->lines_per_frame - 8;
+ __v4l2_ctrl_modify_range(dev->ctrls.exposure, dev->ctrls.exposure->minimum,
+ exposure_max, dev->ctrls.exposure->step,
+ dev->ctrls.exposure->val < exposure_max ?
+ dev->ctrls.exposure->val : exposure_max);
+ }
+
switch (ctrl->id) {
case V4L2_CID_FOCUS_ABSOLUTE:
dev_dbg(&client->dev, "%s: CID_FOCUS_ABSOLUTE:%d.\n",
@@ -1616,6 +1632,7 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
int ret;
int hblank;
int vblank_max, vblank_min, vblank_def;
+ int exposure_max;
ret = v4l2_ctrl_handler_init(&ov5693->ctrl_handler,
ARRAY_SIZE(ov5693_controls));
@@ -1648,10 +1665,10 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
}
/* Exposure */
-
+ exposure_max = ov5693->mode->lines_per_frame - 8;
ov5693->ctrls.exposure = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
- V4L2_CID_EXPOSURE, 16,
- 1048575, 16, 512);
+ V4L2_CID_EXPOSURE, 1,
+ exposure_max, 1, 123);
/* Gain */
--
2.31.1
From 82c812e90f36e647a1b6edc1f89202be0e8c1603 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 10 Feb 2021 00:39:42 +0000
Subject: [PATCH] media: i2c: Fix incorrect bit-setting
The bitmask macros to set the exposure for the ov5693 are not quite right.
Update them so that they're setting the correct bits in the registers.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index cea767230aa9..f681dbfcec56 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -63,11 +63,11 @@ MODULE_PARM_DESC(up_delay,
/* Exposure/gain */
#define OV5693_EXPOSURE_CTRL_HH_REG 0x3500
-#define OV5693_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(18, 16)) >> 16)
+#define OV5693_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(14, 12)) >> 12)
#define OV5693_EXPOSURE_CTRL_H_REG 0x3501
-#define OV5693_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8)
+#define OV5693_EXPOSURE_CTRL_H(v) (((v) & GENMASK(11, 4)) >> 4)
#define OV5693_EXPOSURE_CTRL_L_REG 0x3502
-#define OV5693_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0))
+#define OV5693_EXPOSURE_CTRL_L(v) (((v) & GENMASK(3, 0)) << 4)
#define OV5693_EXPOSURE_GAIN_MANUAL_REG 0x3509
#define OV5693_GAIN_CTRL_H_REG 0x3504
--
2.31.1
From c25f345598a0e098cf40bbfb225f8980ed46d3a5 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 10 Feb 2021 16:25:48 +0000
Subject: [PATCH] media: i2c: Don't set stream on during mode config
Currently the register lists for the ov5693 include setting stream on.
That register shouldn't be set until the control is called, so remove
this setting from all of the modes.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.h | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 9d7eed97963b..965208078c2b 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -581,7 +581,6 @@ static struct ov5693_reg const ov5693_654x496[] = {
{OV5693_8BIT, 0x3820, 0x04},
{OV5693_8BIT, 0x3821, 0x1f},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -626,7 +625,6 @@ static struct ov5693_reg const ov5693_1296x976[] = {
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x00},
{OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -656,7 +654,6 @@ static struct ov5693_reg const ov5693_336x256[] = {
{OV5693_8BIT, 0x3820, 0x04},
{OV5693_8BIT, 0x3821, 0x1f},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -683,7 +680,6 @@ static struct ov5693_reg const ov5693_368x304[] = {
{OV5693_8BIT, 0x3820, 0x04},
{OV5693_8BIT, 0x3821, 0x1f},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -715,7 +711,6 @@ static struct ov5693_reg const ov5693_192x160[] = {
{OV5693_8BIT, 0x3820, 0x04},
{OV5693_8BIT, 0x3821, 0x1f},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -742,7 +737,6 @@ static struct ov5693_reg const ov5693_736x496[] = {
{OV5693_8BIT, 0x3820, 0x04},
{OV5693_8BIT, 0x3821, 0x1f},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
#endif
@@ -771,7 +765,6 @@ static struct ov5693_reg const ov5693_736x496[] = {
{OV5693_8BIT, 0x3820, 0x01},
{OV5693_8BIT, 0x3821, 0x1f},
{OV5693_8BIT, 0x5002, 0x00},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
*/
@@ -802,7 +795,6 @@ static struct ov5693_reg const ov5693_976x556[] = {
{OV5693_8BIT, 0x3820, 0x00},
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -841,7 +833,6 @@ static struct ov5693_reg const ov5693_1296x736[] = {
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x00},
{OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -868,7 +859,6 @@ static struct ov5693_reg const ov5693_1636p_30fps[] = {
{OV5693_8BIT, 0x3820, 0x00},
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
#endif
@@ -904,7 +894,6 @@ static struct ov5693_reg const ov5693_1616x1216_30fps[] = {
{OV5693_8BIT, 0x3821, 0x1e}, /*MIRROR control*/
{OV5693_8BIT, 0x5002, 0x00},
{OV5693_8BIT, 0x5041, 0x84},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -935,7 +924,6 @@ static struct ov5693_reg const ov5693_1940x1096[] = {
{OV5693_8BIT, 0x3820, 0x00},
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x80},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -1029,7 +1017,6 @@ static struct ov5693_reg const ov5693_2592x1944_30fps[] = {
{OV5693_8BIT, 0x3820, 0x00},
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x00},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
#endif
@@ -1073,7 +1060,6 @@ static struct ov5693_reg const ov5693_1424x1168_30fps[] = {
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x00},
{OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
#endif
@@ -1114,7 +1100,6 @@ static struct ov5693_reg const ov5693_736x496_30fps[] = {
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x00},
{OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
@@ -1141,7 +1126,6 @@ static struct ov5693_reg const ov5693_2576x1936_30fps[] = {
{OV5693_8BIT, 0x3820, 0x00},
{OV5693_8BIT, 0x3821, 0x1e},
{OV5693_8BIT, 0x5002, 0x00},
- {OV5693_8BIT, 0x0100, 0x01},
{OV5693_TOK_TERM, 0, 0}
};
--
2.31.1
From a260d023487fd06f331faab704bce1dafc4c2799 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 10 Feb 2021 16:35:24 +0000
Subject: [PATCH] media: i2c: Update gain control for ov5693
The gain control of the ov5693 driver is setting the wrong bits and
defining an invalid maximum value; change (and use) the bitshifting
macros and update the control's ranges.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index f681dbfcec56..51eb3b05d121 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -71,9 +71,9 @@ MODULE_PARM_DESC(up_delay,
#define OV5693_EXPOSURE_GAIN_MANUAL_REG 0x3509
#define OV5693_GAIN_CTRL_H_REG 0x3504
-#define OV5693_GAIN_CTRL_H(v) (((v) & GENMASK(9, 8)) >> 8)
+#define OV5693_GAIN_CTRL_H(v) ((v >> 4) & GENMASK(2, 0))
#define OV5693_GAIN_CTRL_L_REG 0x3505
-#define OV5693_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0))
+#define OV5693_GAIN_CTRL_L(v) ((v << 4) & GENMASK(7, 4))
#define OV5693_FORMAT1_REG 0x3820
#define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(2)
@@ -889,9 +889,13 @@ static int ov5693_analog_gain_configure(struct ov5693_device *sensor, u32 gain)
{
int ret;
- /* Analog gain */
+ /*
+ * As with exposure, the lowest 4 bits are fractional bits. Setting
+ * those is not supported, so we have a tiny bit of bit shifting to
+ * do.
+ */
ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
- OV5693_AGC_L, gain & 0xff);
+ OV5693_AGC_L, OV5693_GAIN_CTRL_L(gain));
if (ret) {
dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_AGC_L);
@@ -899,7 +903,7 @@ static int ov5693_analog_gain_configure(struct ov5693_device *sensor, u32 gain)
}
ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
- OV5693_AGC_H, (gain >> 8) & 0xff);
+ OV5693_AGC_H, OV5693_GAIN_CTRL_H(gain));
if (ret) {
dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_AGC_H);
@@ -1674,10 +1678,10 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
ov5693->ctrls.analogue_gain = v4l2_ctrl_new_std(&ov5693->ctrl_handler,
ops, V4L2_CID_ANALOGUE_GAIN,
- 1, 1023, 1, 128);
+ 1, 127, 1, 8);
ov5693->ctrls.digital_gain = v4l2_ctrl_new_std(&ov5693->ctrl_handler, ops,
V4L2_CID_DIGITAL_GAIN, 1,
- 3999, 1, 1000);
+ 4095, 1, 1024);
/* Flip */
--
2.31.1
From 517edadd896692e5e5ed67947753c6fec5fe2a57 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Wed, 10 Feb 2021 23:44:39 +0000
Subject: [PATCH] media: i2c: Fixup gain read
This function reads the bits from the gain registers poorly. Update
it to do that properly (although, it probably just needs to be deleted)
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 51eb3b05d121..952558c4f33b 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -770,30 +770,35 @@ static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
static int ov5693_get_exposure(struct ov5693_device *sensor)
{
- u16 reg_v, reg_v2;
+ u32 exposure = 0;
+ u16 tmp;
int ret = 0;
/* get exposure */
ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
OV5693_EXPOSURE_L,
- &reg_v);
+ &tmp);
if (ret)
return ret;
+ exposure |= ((tmp >> 4) & 0b1111);
+
ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
OV5693_EXPOSURE_M,
- &reg_v2);
+ &tmp);
if (ret)
return ret;
- reg_v += reg_v2 << 8;
+ exposure |= (tmp << 4);
ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
OV5693_EXPOSURE_H,
- &reg_v2);
+ &tmp);
if (ret)
return ret;
- printk("exposure set to: %u\n", reg_v + (((u32)reg_v2 << 16)));
+ exposure |= (tmp << 12);
+
+ printk("exposure set to: %u\n", exposure);
return ret;
}
--
2.31.1
From 2caa720896232eb723bfa24b8c1b76e8b78a3789 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 11 Feb 2021 00:40:10 +0000
Subject: [PATCH] media: i2c: Update controls on stream
Currently the ov5693 driver throws away control setting by simply loading
each mode's default registers. Instead, re-set the user defined controls
during stream with __v4l2_ctrl_handler_setup()
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 952558c4f33b..dd31083eeb7b 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1492,6 +1492,12 @@ static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
}
}
+ ret = __v4l2_ctrl_handler_setup(&dev->ctrl_handler);
+ if (ret) {
+ power_down(sd);
+ return ret;
+ }
+
ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
enable ? OV5693_START_STREAMING :
OV5693_STOP_STREAMING);
--
2.31.1
From 650d1eef03996a358d72746472ee5f44a7a15383 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 11 Feb 2021 23:29:15 +0000
Subject: [PATCH] media: i2c: Correct link frequency value
The link frequency is given by vts * hts * fps * bits / lanes / 2. In the
case of the ov5693 driver that works out to 400MHz, not 640Mhz. Correct
the macro.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 965208078c2b..7f1d31a82d3d 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -186,13 +186,13 @@
#define OV5693_OTP_MODE_READ 1
/* link freq and pixel rate required for IPU3 */
-#define OV5693_LINK_FREQ_640MHZ 640000000
+#define OV5693_LINK_FREQ_400MHZ 400000000
/* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample
* To avoid integer overflow, dividing by bits_per_sample first.
*/
-#define OV5693_PIXEL_RATE (OV5693_LINK_FREQ_640MHZ / 10) * 2 * 2
+#define OV5693_PIXEL_RATE (OV5693_LINK_FREQ_400MHZ / 10) * 2 * 2
static const s64 link_freq_menu_items[] = {
- OV5693_LINK_FREQ_640MHZ
+ OV5693_LINK_FREQ_400MHZ
};
#define OV5693_NUM_SUPPLIES 2
--
2.31.1
From 19ed6b37c6757afd6aebe17b3949563ce6f1c674 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Mon, 25 Jan 2021 23:12:09 +0000
Subject: [PATCH] media: i2c: Cleanup ov5693 driver
This commit performs some cleanup to the ov5693 driver:
1. Superfluous words in variable names dropped; "i2c_client" becomes
"client", "input_lock" becomes "lock"
2. ov5693_configure_gpios() is does error handling properly, and uses
gpiod_get_optional()
3. The name of the struct ov5693_device variable in each functions, which
previously was a mix of dev, sensor or ov5693, is standardised to the
latter.
4. The list of headers is alphabetised (and probably also needs trimming)
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 309 +++++++++++++++++++------------------
drivers/media/i2c/ov5693.h | 5 +-
2 files changed, 165 insertions(+), 149 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index dd31083eeb7b..0643390c872a 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -16,25 +16,25 @@
*
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/kmod.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/mm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
-#include <linux/io.h>
-#include <linux/acpi.h>
-#include <linux/regulator/consumer.h>
#include "ov5693.h"
#include "ad5823.h"
@@ -485,12 +485,12 @@ static int ov5693_read_otp_reg_array(struct i2c_client *client, u16 size,
static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
int ret;
int i;
u8 *b = buf;
- dev->otp_size = 0;
+ ov5693->otp_size = 0;
for (i = 1; i < OV5693_OTP_BANK_MAX; i++) {
/*set bank NO and OTP read mode. */
ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_OTP_BANK_REG,
@@ -529,7 +529,7 @@ static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf)
//Intel OTP map, try to read 320byts first.
if (i == 21) {
if ((*b) == 0) {
- dev->otp_size = 320;
+ ov5693->otp_size = 320;
break;
}
/* (*b) != 0 */
@@ -538,7 +538,7 @@ static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf)
} else if (i ==
24) { //if the first 320bytes data doesn't not exist, try to read the next 32bytes data.
if ((*b) == 0) {
- dev->otp_size = 32;
+ ov5693->otp_size = 32;
break;
}
/* (*b) != 0 */
@@ -547,11 +547,11 @@ static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf)
} else if (i ==
27) { //if the prvious 32bytes data doesn't exist, try to read the next 32bytes data again.
if ((*b) == 0) {
- dev->otp_size = 32;
+ ov5693->otp_size = 32;
break;
}
/* (*b) != 0 */
- dev->otp_size = 0; // no OTP data.
+ ov5693->otp_size = 0; // no OTP data.
break;
}
@@ -598,20 +598,20 @@ static void *ov5693_otp_read(struct v4l2_subdev *sd)
return buf;
}
-static int ov5693_update_bits(struct ov5693_device *sensor, u16 address,
+static int ov5693_update_bits(struct ov5693_device *ov5693, u16 address,
u16 mask, u16 bits)
{
u16 value = 0;
int ret;
- ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT, address, &value);
+ ret = ov5693_read_reg(ov5693->client, OV5693_8BIT, address, &value);
if (ret)
return ret;
value &= ~mask;
value |= bits;
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT, address, value);
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT, address, value);
if (ret)
return ret;
@@ -620,13 +620,13 @@ static int ov5693_update_bits(struct ov5693_device *sensor, u16 address,
/* Flip */
-static int ov5693_flip_vert_configure(struct ov5693_device *sensor, bool enable)
+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(sensor, OV5693_FORMAT1_REG, bits,
+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, bits,
enable ? bits : 0);
if (ret)
return ret;
@@ -634,13 +634,13 @@ static int ov5693_flip_vert_configure(struct ov5693_device *sensor, bool enable)
return 0;
}
-static int ov5693_flip_horz_configure(struct ov5693_device *sensor, bool enable)
+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(sensor, OV5693_FORMAT2_REG, bits,
+ ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, bits,
enable ? bits : 0);
if (ret)
return ret;
@@ -721,14 +721,14 @@ static int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value)
static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
dev_dbg(&client->dev, "%s: FOCUS_POS: 0x%x\n", __func__, value);
value = clamp(value, 0, OV5693_VCM_MAX_FOCUS_POS);
- if (dev->vcm == VCM_DW9714) {
- if (dev->vcm_update) {
+ if (ov5693->vcm == VCM_DW9714) {
+ if (ov5693->vcm_update) {
ret = vcm_dw_i2c_write(client, VCM_PROTECTION_OFF);
if (ret)
return ret;
@@ -738,17 +738,17 @@ static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value)
ret = vcm_dw_i2c_write(client, VCM_PROTECTION_ON);
if (ret)
return ret;
- dev->vcm_update = false;
+ ov5693->vcm_update = false;
}
ret = vcm_dw_i2c_write(client,
vcm_val(value, VCM_DEFAULT_S));
- } else if (dev->vcm == VCM_AD5823) {
+ } else if (ov5693->vcm == VCM_AD5823) {
ad5823_t_focus_abs(sd, value);
}
if (ret == 0) {
- dev->number_of_steps = value - dev->focus;
- dev->focus = value;
- dev->timestamp_t_focus_abs = ktime_get();
+ ov5693->number_of_steps = value - ov5693->focus;
+ ov5693->focus = value;
+ ov5693->timestamp_t_focus_abs = ktime_get();
} else
dev_err(&client->dev,
"%s: i2c failed. ret %d\n", __func__, ret);
@@ -758,9 +758,9 @@ static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value)
static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- return ov5693_t_focus_abs(sd, dev->focus + value);
+ return ov5693_t_focus_abs(sd, ov5693->focus + value);
}
#define DELAY_PER_STEP_NS 1000000
@@ -768,14 +768,14 @@ static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
/* Exposure */
-static int ov5693_get_exposure(struct ov5693_device *sensor)
+static int ov5693_get_exposure(struct ov5693_device *ov5693)
{
u32 exposure = 0;
u16 tmp;
int ret = 0;
/* get exposure */
- ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_L,
&tmp);
if (ret)
@@ -783,14 +783,14 @@ static int ov5693_get_exposure(struct ov5693_device *sensor)
exposure |= ((tmp >> 4) & 0b1111);
- ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_M,
&tmp);
if (ret)
return ret;
exposure |= (tmp << 4);
- ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_H,
&tmp);
if (ret)
@@ -802,7 +802,7 @@ static int ov5693_get_exposure(struct ov5693_device *sensor)
return ret;
}
-static int ov5693_exposure_configure(struct ov5693_device *sensor, u32 exposure)
+static int ov5693_exposure_configure(struct ov5693_device *ov5693, u32 exposure)
{
int ret;
@@ -812,40 +812,40 @@ static int ov5693_exposure_configure(struct ov5693_device *sensor, u32 exposure)
*/
exposure = exposure * 16;
- ov5693_get_exposure(sensor);
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ ov5693_get_exposure(ov5693);
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_CTRL_HH_REG, OV5693_EXPOSURE_CTRL_HH(exposure));
if (ret)
return ret;
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_CTRL_H_REG, OV5693_EXPOSURE_CTRL_H(exposure));
if (ret)
return ret;
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_CTRL_L_REG, OV5693_EXPOSURE_CTRL_L(exposure));
if (ret)
return ret;
- ov5693_get_exposure(sensor);
+ ov5693_get_exposure(ov5693);
return 0;
}
/* Gain */
-static int ov5693_get_gain(struct ov5693_device *sensor, u32 *gain)
+static int ov5693_get_gain(struct ov5693_device *ov5693, u32 *gain)
{
u16 gain_l, gain_h;
int ret = 0;
- ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
OV5693_GAIN_CTRL_L_REG,
&gain_l);
if (ret)
return ret;
- ret = ov5693_read_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
OV5693_GAIN_CTRL_H_REG,
&gain_h);
if (ret)
@@ -856,33 +856,33 @@ static int ov5693_get_gain(struct ov5693_device *sensor, u32 *gain)
return ret;
}
-static int ov5693_gain_configure(struct ov5693_device *sensor, u32 gain)
+static int ov5693_gain_configure(struct ov5693_device *ov5693, u32 gain)
{
int ret;
/* A 1.0 gain is 0x400 */
gain = (gain * 1024)/1000;
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_16BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_16BIT,
OV5693_MWB_RED_GAIN_H, gain);
if (ret) {
- dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ dev_err(&ov5693->client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_MWB_RED_GAIN_H);
return ret;
}
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_16BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_16BIT,
OV5693_MWB_GREEN_GAIN_H, gain);
if (ret) {
- dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ dev_err(&ov5693->client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_MWB_RED_GAIN_H);
return ret;
}
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_16BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_16BIT,
OV5693_MWB_BLUE_GAIN_H, gain);
if (ret) {
- dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ dev_err(&ov5693->client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_MWB_RED_GAIN_H);
return ret;
}
@@ -890,7 +890,7 @@ static int ov5693_gain_configure(struct ov5693_device *sensor, u32 gain)
return 0;
}
-static int ov5693_analog_gain_configure(struct ov5693_device *sensor, u32 gain)
+static int ov5693_analog_gain_configure(struct ov5693_device *ov5693, u32 gain)
{
int ret;
@@ -899,18 +899,18 @@ static int ov5693_analog_gain_configure(struct ov5693_device *sensor, u32 gain)
* those is not supported, so we have a tiny bit of bit shifting to
* do.
*/
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_AGC_L, OV5693_GAIN_CTRL_L(gain));
if (ret) {
- dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ dev_err(&ov5693->client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_AGC_L);
return ret;
}
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_AGC_H, OV5693_GAIN_CTRL_H(gain));
if (ret) {
- dev_err(&sensor->i2c_client->dev, "%s: write %x error, aborted\n",
+ dev_err(&ov5693->client->dev, "%s: write %x error, aborted\n",
__func__, OV5693_AGC_H);
return ret;
}
@@ -920,60 +920,60 @@ static int ov5693_analog_gain_configure(struct ov5693_device *sensor, u32 gain)
static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct ov5693_device *dev =
+ struct ov5693_device *ov5693 =
container_of(ctrl->handler, struct ov5693_device, ctrl_handler);
- struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&ov5693->sd);
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 = dev->mode->lines_per_frame - 8;
- __v4l2_ctrl_modify_range(dev->ctrls.exposure, dev->ctrls.exposure->minimum,
- exposure_max, dev->ctrls.exposure->step,
- dev->ctrls.exposure->val < exposure_max ?
- dev->ctrls.exposure->val : exposure_max);
+ exposure_max = ov5693->mode->lines_per_frame - 8;
+ __v4l2_ctrl_modify_range(ov5693->ctrls.exposure, ov5693->ctrls.exposure->minimum,
+ exposure_max, ov5693->ctrls.exposure->step,
+ ov5693->ctrls.exposure->val < exposure_max ?
+ ov5693->ctrls.exposure->val : exposure_max);
}
switch (ctrl->id) {
case V4L2_CID_FOCUS_ABSOLUTE:
dev_dbg(&client->dev, "%s: CID_FOCUS_ABSOLUTE:%d.\n",
__func__, ctrl->val);
- ret = ov5693_t_focus_abs(&dev->sd, ctrl->val);
+ ret = ov5693_t_focus_abs(&ov5693->sd, ctrl->val);
break;
case V4L2_CID_FOCUS_RELATIVE:
dev_dbg(&client->dev, "%s: CID_FOCUS_RELATIVE:%d.\n",
__func__, ctrl->val);
- ret = ov5693_t_focus_rel(&dev->sd, ctrl->val);
+ ret = ov5693_t_focus_rel(&ov5693->sd, ctrl->val);
break;
case V4L2_CID_EXPOSURE:
dev_dbg(&client->dev, "%s: CID_EXPOSURE:%d.\n",
__func__, ctrl->val);
- ret = ov5693_exposure_configure(dev, ctrl->val);
+ ret = ov5693_exposure_configure(ov5693, ctrl->val);
if (ret)
return ret;
break;
case V4L2_CID_ANALOGUE_GAIN:
dev_dbg(&client->dev, "%s: CID_ANALOGUE_GAIN:%d.\n",
__func__, ctrl->val);
- ret = ov5693_analog_gain_configure(dev, ctrl->val);
+ ret = ov5693_analog_gain_configure(ov5693, ctrl->val);
if (ret)
return ret;
break;
case V4L2_CID_DIGITAL_GAIN:
dev_dbg(&client->dev, "%s: CID_DIGITAL_GAIN:%d.\n",
__func__, ctrl->val);
- ret = ov5693_gain_configure(dev, ctrl->val);
+ ret = ov5693_gain_configure(ov5693, ctrl->val);
if (ret)
return ret;
break;
case V4L2_CID_HFLIP:
- return ov5693_flip_horz_configure(dev, !!ctrl->val);
+ return ov5693_flip_horz_configure(ov5693, !!ctrl->val);
case V4L2_CID_VFLIP:
- return ov5693_flip_vert_configure(dev, !!ctrl->val);
+ return ov5693_flip_vert_configure(ov5693, !!ctrl->val);
case V4L2_CID_VBLANK:
ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_TIMING_VTS_H,
- dev->mode->height + ctrl->val);
+ ov5693->mode->height + ctrl->val);
break;
default:
ret = -EINVAL;
@@ -983,16 +983,16 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
static int ov5693_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct ov5693_device *dev =
+ struct ov5693_device *ov5693 =
container_of(ctrl->handler, struct ov5693_device, ctrl_handler);
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_EXPOSURE_ABSOLUTE:
- ret = ov5693_q_exposure(&dev->sd, &ctrl->val);
+ ret = ov5693_q_exposure(&ov5693->sd, &ctrl->val);
break;
case V4L2_CID_AUTOGAIN:
- ret = ov5693_get_gain(dev, &ctrl->val);
+ ret = ov5693_get_gain(ov5693, &ctrl->val);
break;
case V4L2_CID_FOCUS_ABSOLUTE:
/* NOTE: there was atomisp-specific function ov5693_q_focus_abs() */
@@ -1034,12 +1034,12 @@ static const struct v4l2_ctrl_config ov5693_controls[] = {
},
};
-static int ov5693_isp_configure(struct ov5693_device *sensor)
+static int ov5693_isp_configure(struct ov5693_device *ov5693)
{
int ret;
/* Enable lens correction. */
- ret = ov5693_write_reg(sensor->i2c_client, OV5693_8BIT,
+ ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_ISP_CTRL0_REG, 0x86);
if (ret)
return ret;
@@ -1049,18 +1049,18 @@ static int ov5693_isp_configure(struct ov5693_device *sensor)
static int ov5693_init(struct v4l2_subdev *sd)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
- if (!dev->has_vcm)
+ if (!ov5693->has_vcm)
return 0;
dev_info(&client->dev, "%s\n", __func__);
- mutex_lock(&dev->input_lock);
- dev->vcm_update = false;
+ mutex_lock(&ov5693->lock);
+ ov5693->vcm_update = false;
- if (dev->vcm == VCM_AD5823) {
+ if (ov5693->vcm == VCM_AD5823) {
ret = vcm_ad_i2c_wr8(client, 0x01, 0x01); /* vcm init test */
if (ret)
dev_err(&client->dev,
@@ -1079,16 +1079,16 @@ static int ov5693_init(struct v4l2_subdev *sd)
}
/*change initial focus value for ad5823*/
- if (dev->vcm == VCM_AD5823) {
- dev->focus = AD5823_INIT_FOCUS_POS;
+ if (ov5693->vcm == VCM_AD5823) {
+ ov5693->focus = AD5823_INIT_FOCUS_POS;
ov5693_t_focus_abs(sd, AD5823_INIT_FOCUS_POS);
} else {
- dev->focus = 0;
+ ov5693->focus = 0;
ov5693_t_focus_abs(sd, 0);
}
- ov5693_isp_configure(dev);
- mutex_unlock(&dev->input_lock);
+ ov5693_isp_configure(ov5693);
+ mutex_unlock(&ov5693->lock);
return 0;
}
@@ -1096,32 +1096,32 @@ static int ov5693_init(struct v4l2_subdev *sd)
static int __power_up(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5693_device *sensor = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
int ret;
- ret = clk_prepare_enable(sensor->clk);
+ ret = clk_prepare_enable(ov5693->clk);
if (ret) {
dev_err(&client->dev, "Error enabling clock\n");
return -EINVAL;
}
- if (sensor->indicator_led)
- gpiod_set_value_cansleep(sensor->indicator_led, 1);
+ if (ov5693->indicator_led)
+ gpiod_set_value_cansleep(ov5693->indicator_led, 1);
ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES,
- sensor->supplies);
+ ov5693->supplies);
if (ret)
goto fail_power;
- gpiod_set_value_cansleep(sensor->reset, 0);
+ gpiod_set_value_cansleep(ov5693->reset, 0);
__cci_delay(up_delay);
return 0;
fail_power:
- if (sensor->indicator_led)
- gpiod_set_value_cansleep(sensor->indicator_led, 0);
+ if (ov5693->indicator_led)
+ gpiod_set_value_cansleep(ov5693->indicator_led, 0);
dev_err(&client->dev, "sensor power-up failed\n");
return ret;
@@ -1129,17 +1129,17 @@ static int __power_up(struct v4l2_subdev *sd)
static int power_down(struct v4l2_subdev *sd)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- dev->focus = OV5693_INVALID_CONFIG;
+ ov5693->focus = OV5693_INVALID_CONFIG;
- gpiod_set_value_cansleep(dev->reset, 1);
+ gpiod_set_value_cansleep(ov5693->reset, 1);
- clk_disable_unprepare(dev->clk);
+ clk_disable_unprepare(ov5693->clk);
- if (dev->indicator_led)
- gpiod_set_value_cansleep(dev->indicator_led, 0);
- return regulator_bulk_disable(OV5693_NUM_SUPPLIES, dev->supplies);
+ if (ov5693->indicator_led)
+ gpiod_set_value_cansleep(ov5693->indicator_led, 0);
+ return regulator_bulk_disable(OV5693_NUM_SUPPLIES, ov5693->supplies);
}
static int power_up(struct v4l2_subdev *sd)
@@ -1265,7 +1265,7 @@ static int get_resolution_index(int w, int h)
/* TODO: remove it. */
static int startup(struct v4l2_subdev *sd)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
@@ -1282,7 +1282,7 @@ static int startup(struct v4l2_subdev *sd)
return ret;
}
- ret = ov5693_write_reg_array(client, ov5693_res[dev->fmt_idx].regs);
+ ret = ov5693_write_reg_array(client, ov5693_res[ov5693->fmt_idx].regs);
if (ret) {
dev_err(&client->dev, "ov5693 write register err.\n");
return ret;
@@ -1296,7 +1296,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt = &format->format;
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
int idx;
@@ -1307,7 +1307,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
if (!fmt)
return -EINVAL;
- mutex_lock(&dev->input_lock);
+ mutex_lock(&ov5693->lock);
idx = nearest_resolution_index(fmt->width, fmt->height);
if (idx == -1) {
/* return the largest resolution */
@@ -1325,8 +1325,8 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
goto mutex_unlock;
}
- dev->fmt_idx = get_resolution_index(fmt->width, fmt->height);
- if (dev->fmt_idx == -1) {
+ ov5693->fmt_idx = get_resolution_index(fmt->width, fmt->height);
+ if (ov5693->fmt_idx == -1) {
dev_err(&client->dev, "get resolution fail\n");
ret = -EINVAL;
goto mutex_unlock;
@@ -1339,9 +1339,9 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
continue;
}
- mutex_unlock(&dev->input_lock);
+ mutex_unlock(&ov5693->lock);
ov5693_init(sd);
- mutex_lock(&dev->input_lock);
+ mutex_lock(&ov5693->lock);
ret = startup(sd);
if (ret)
dev_err(&client->dev, " startup() FAILED!\n");
@@ -1353,8 +1353,6 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
goto mutex_unlock;
}
-
-
/*
* After sensor settings are set to HW, sometimes stream is started.
* This would cause ISP timeout because ISP is not ready to receive
@@ -1366,19 +1364,19 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
dev_warn(&client->dev, "ov5693 stream off err\n");
mutex_unlock:
- mutex_unlock(&dev->input_lock);
+ mutex_unlock(&ov5693->lock);
return ret;
}
static const struct v4l2_rect *
-__ov5693_get_pad_crop(struct ov5693_device *dev, struct v4l2_subdev_pad_config *cfg,
+__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(&dev->sd, cfg, pad);
+ return v4l2_subdev_get_try_crop(&ov5693->sd, cfg, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &dev->mode->crop;
+ return &ov5693->mode->crop;
}
return NULL;
@@ -1389,12 +1387,12 @@ static int ov5693_get_selection(struct v4l2_subdev *sd,
{
switch (sel->target) {
case V4L2_SEL_TGT_CROP: {
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- mutex_lock(&dev->input_lock);
- sel->r = *__ov5693_get_pad_crop(dev, cfg, sel->pad,
+ mutex_lock(&ov5693->lock);
+ sel->r = *__ov5693_get_pad_crop(ov5693, cfg, sel->pad,
sel->which);
- mutex_unlock(&dev->input_lock);
+ mutex_unlock(&ov5693->lock);
return 0;
}
@@ -1424,7 +1422,7 @@ static int ov5693_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt = &format->format;
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
if (format->pad)
return -EINVAL;
@@ -1432,8 +1430,8 @@ static int ov5693_get_fmt(struct v4l2_subdev *sd,
if (!fmt)
return -EINVAL;
- fmt->width = ov5693_res[dev->fmt_idx].width;
- fmt->height = ov5693_res[dev->fmt_idx].height;
+ fmt->width = ov5693_res[ov5693->fmt_idx].width;
+ fmt->height = ov5693_res[ov5693->fmt_idx].height;
fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
return 0;
@@ -1481,7 +1479,7 @@ static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
- mutex_lock(&dev->input_lock);
+ mutex_lock(&dev->lock);
/* power_on() here before streaming for regular PCs. */
if (enable) {
@@ -1507,26 +1505,26 @@ static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
power_down(sd);
out:
- mutex_unlock(&dev->input_lock);
+ mutex_unlock(&dev->lock);
return ret;
}
static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
- mutex_lock(&dev->input_lock);
+ mutex_lock(&ov5693->lock);
ret = power_up(sd);
if (ret) {
dev_err(&client->dev, "ov5693 power-up err.\n");
goto fail_power_on;
}
- if (!dev->vcm)
- dev->vcm = vcm_detect(client);
+ if (!ov5693->vcm)
+ ov5693->vcm = vcm_detect(client);
/* config & detect sensor */
ret = ov5693_detect(client);
@@ -1535,7 +1533,7 @@ static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
goto fail_power_on;
}
- dev->otp_data = ov5693_otp_read(sd);
+ ov5693->otp_data = ov5693_otp_read(sd);
/* turn off sensor, after probed */
ret = power_down(sd);
@@ -1543,24 +1541,24 @@ static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
dev_err(&client->dev, "ov5693 power-off err.\n");
goto fail_power_on;
}
- mutex_unlock(&dev->input_lock);
+ mutex_unlock(&ov5693->lock);
return ret;
fail_power_on:
power_down(sd);
dev_err(&client->dev, "sensor power-gating failed\n");
- mutex_unlock(&dev->input_lock);
+ mutex_unlock(&ov5693->lock);
return ret;
}
static int ov5693_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
interval->interval.numerator = 1;
- interval->interval.denominator = ov5693_res[dev->fmt_idx].fps;
+ interval->interval.denominator = ov5693_res[ov5693->fmt_idx].fps;
return 0;
}
@@ -1725,7 +1723,7 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
return ret;
/* Use same lock for controls as for everything else. */
- ov5693->ctrl_handler.lock = &ov5693->input_lock;
+ ov5693->ctrl_handler.lock = &ov5693->lock;
ov5693->sd.ctrl_handler = &ov5693->ctrl_handler;
return 0;
@@ -1733,21 +1731,38 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
static int ov5693_configure_gpios(struct ov5693_device *ov5693)
{
- ov5693->reset = gpiod_get_index(&ov5693->i2c_client->dev, "reset", 0,
+ int ret;
+
+ ov5693->reset = gpiod_get_optional(&ov5693->client->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(ov5693->reset)) {
- dev_err(&ov5693->i2c_client->dev, "Couldn't find reset GPIO\n");
- return -EINVAL;
+ dev_err(&ov5693->client->dev, "Couldn't find reset GPIO\n");
+ return PTR_ERR(ov5693->reset);
+ }
+
+ ov5693->powerdown = gpiod_get_optional(&ov5693->client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov5693->powerdown)) {
+ dev_err(&ov5693->client->dev, "Couldn't find powerdown GPIO\n");
+ ret = PTR_ERR(ov5693->powerdown);
+ goto err_put_reset;
}
- ov5693->indicator_led = gpiod_get_index_optional(&ov5693->i2c_client->dev, "indicator-led", 0,
+ ov5693->indicator_led = gpiod_get_optional(&ov5693->client->dev, "indicator-led",
GPIOD_OUT_HIGH);
if (IS_ERR(ov5693->indicator_led)) {
- dev_err(&ov5693->i2c_client->dev, "Couldn't find indicator-led GPIO\n");
- return -EINVAL;
+ dev_err(&ov5693->client->dev, "Couldn't find indicator-led GPIO\n");
+ ret = PTR_ERR(ov5693->indicator_led);
+ goto err_put_powerdown;
}
return 0;
+err_put_reset:
+ gpiod_put(ov5693->reset);
+err_put_powerdown:
+ gpiod_put(ov5693->powerdown);
+
+ return ret;
}
static int ov5693_get_regulators(struct ov5693_device *ov5693)
@@ -1757,7 +1772,7 @@ static int ov5693_get_regulators(struct ov5693_device *ov5693)
for (i = 0; i < OV5693_NUM_SUPPLIES; i++)
ov5693->supplies[i].supply = ov5693_supply_names[i];
- return regulator_bulk_get(&ov5693->i2c_client->dev,
+ return regulator_bulk_get(&ov5693->client->dev,
OV5693_NUM_SUPPLIES,
ov5693->supplies);
}
@@ -1773,13 +1788,13 @@ static int ov5693_probe(struct i2c_client *client)
if (!ov5693)
return -ENOMEM;
- ov5693->i2c_client = client;
+ ov5693->client = client;
/* check if VCM device exists */
/* TODO: read from SSDB */
ov5693->has_vcm = false;
- mutex_init(&ov5693->input_lock);
+ mutex_init(&ov5693->lock);
v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops);
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 7f1d31a82d3d..70ccb3aae4c7 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -241,14 +241,15 @@ enum vcm_type {
* ov5693 device structure.
*/
struct ov5693_device {
- struct i2c_client *i2c_client;
+ struct i2c_client *client;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_mbus_framefmt format;
- struct mutex input_lock;
+ struct mutex lock;
struct v4l2_ctrl_handler ctrl_handler;
struct gpio_desc *reset;
+ struct gpio_desc *powerdown;
struct gpio_desc *indicator_led;
struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES];
struct clk *clk;
--
2.31.1
From 94455706c3081720558c506713f197bfcedf564c Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 28 Jan 2021 12:04:38 +0000
Subject: [PATCH] media: i2c: Add pm_runtime support to ov5693 driver
The ov5693 driver currently uses hacky and horrible power up/down methods
called directly in s_stream. Add pm_runtime support and use that in
s_stream instead. Replace all other uses of the power+up/down() calls with
the single ov5693_sensor_stream() for now.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 183 +++++++++++++++++++++++++++++--------
drivers/media/i2c/ov5693.h | 1 +
2 files changed, 146 insertions(+), 38 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 0643390c872a..f2eaa5f71a31 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mm.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -935,6 +936,10 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
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->client->dev))
+ return 0;
+
switch (ctrl->id) {
case V4L2_CID_FOCUS_ABSOLUTE:
dev_dbg(&client->dev, "%s: CID_FOCUS_ABSOLUTE:%d.\n",
@@ -950,27 +955,23 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
dev_dbg(&client->dev, "%s: CID_EXPOSURE:%d.\n",
__func__, ctrl->val);
ret = ov5693_exposure_configure(ov5693, ctrl->val);
- if (ret)
- return ret;
break;
case V4L2_CID_ANALOGUE_GAIN:
dev_dbg(&client->dev, "%s: CID_ANALOGUE_GAIN:%d.\n",
__func__, ctrl->val);
ret = ov5693_analog_gain_configure(ov5693, ctrl->val);
- if (ret)
- return ret;
break;
case V4L2_CID_DIGITAL_GAIN:
dev_dbg(&client->dev, "%s: CID_DIGITAL_GAIN:%d.\n",
__func__, ctrl->val);
ret = ov5693_gain_configure(ov5693, ctrl->val);
- if (ret)
- return ret;
break;
case V4L2_CID_HFLIP:
- return ov5693_flip_horz_configure(ov5693, !!ctrl->val);
+ ret = ov5693_flip_horz_configure(ov5693, !!ctrl->val);
+ break;
case V4L2_CID_VFLIP:
- return ov5693_flip_vert_configure(ov5693, !!ctrl->val);
+ ret = ov5693_flip_vert_configure(ov5693, !!ctrl->val);
+ break;
case V4L2_CID_VBLANK:
ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_TIMING_VTS_H,
ov5693->mode->height + ctrl->val);
@@ -978,6 +979,9 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
default:
ret = -EINVAL;
}
+
+ pm_runtime_put(&ov5693->client->dev);
+
return ret;
}
@@ -1093,6 +1097,106 @@ static int ov5693_init(struct v4l2_subdev *sd)
return 0;
}
+static int ov5693_sw_standby(struct ov5693_device *ov5693, bool standby)
+{
+ return ov5693_write_reg(ov5693->client, OV5693_8BIT, OV5693_SW_STREAM,
+ standby ? OV5693_STOP_STREAMING : OV5693_START_STREAMING);
+}
+
+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);
+ gpiod_set_value_cansleep(ov5693->indicator_led, 0);
+}
+
+
+static int ov5693_sensor_powerup(struct ov5693_device *ov5693)
+{
+ int ret = 0;
+
+ 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->client->dev, "Failed to enable clk\n");
+ goto fail_power;
+ }
+
+ ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES, ov5693->supplies);
+ if (ret) {
+ dev_err(&ov5693->client->dev, "Failed to enable regulators\n");
+ goto fail_power;
+ }
+
+ gpiod_set_value_cansleep(ov5693->reset, 0);
+ gpiod_set_value_cansleep(ov5693->powerdown, 0);
+ gpiod_set_value_cansleep(ov5693->indicator_led, 1);
+
+ usleep_range(20000, 25000);
+
+ return 0;
+
+fail_power:
+ ov5693_sensor_powerdown(ov5693);
+ return ret;
+}
+
+static int __maybe_unused ov5693_sensor_suspend(struct device *dev)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ int ret;
+
+ mutex_lock(&ov5693->lock);
+
+ if (ov5693->streaming) {
+ ret = ov5693_sw_standby(ov5693, true);
+ if (ret)
+ goto out_unlock;
+ }
+
+ ov5693_sensor_powerdown(ov5693);
+
+out_unlock:
+ mutex_unlock(&ov5693->lock);
+ return ret;
+}
+
+static int __maybe_unused ov5693_sensor_resume(struct device *dev)
+{
+ struct i2c_client *client = i2c_verify_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
+ int ret;
+
+ mutex_lock(&ov5693->lock);
+
+ ret = ov5693_sensor_powerup(ov5693);
+ if (ret)
+ goto out_unlock;
+
+ if (ov5693->streaming) {
+ ret = ov5693_sw_standby(ov5693, false);
+ if (ret)
+ goto err_power;
+ }
+
+ goto out_unlock;
+
+err_power:
+ ov5693_sensor_powerdown(ov5693);
+out_unlock:
+ mutex_unlock(&ov5693->lock);
+ return ret;
+}
+
static int __power_up(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -1134,6 +1238,7 @@ static int power_down(struct v4l2_subdev *sd)
ov5693->focus = OV5693_INVALID_CONFIG;
gpiod_set_value_cansleep(ov5693->reset, 1);
+ gpiod_set_value_cansleep(ov5693->powerdown, 1);
clk_disable_unprepare(ov5693->clk);
@@ -1333,7 +1438,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
}
for (cnt = 0; cnt < OV5693_POWER_UP_RETRY_NUM; cnt++) {
- ret = power_up(sd);
+ ret = ov5693_sensor_powerup(ov5693);
if (ret) {
dev_err(&client->dev, "power up failed\n");
continue;
@@ -1475,38 +1580,34 @@ static int ov5693_detect(struct i2c_client *client)
static int ov5693_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
int ret;
- mutex_lock(&dev->lock);
-
- /* power_on() here before streaming for regular PCs. */
if (enable) {
- ret = power_up(sd);
- if (ret) {
- dev_err(&client->dev, "sensor power-up error\n");
- goto out;
- }
+ ret = pm_runtime_get_sync(&ov5693->client->dev);
+ if (ret < 0)
+ goto err_power_down;
}
- ret = __v4l2_ctrl_handler_setup(&dev->ctrl_handler);
- if (ret) {
- power_down(sd);
- return ret;
- }
+ ret = __v4l2_ctrl_handler_setup(&ov5693->ctrl_handler);
+ if (ret)
+ goto err_power_down;
- ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
- enable ? OV5693_START_STREAMING :
- OV5693_STOP_STREAMING);
+ mutex_lock(&ov5693->lock);
+ ret = ov5693_sw_standby(ov5693, !enable);
+ mutex_unlock(&ov5693->lock);
+
+ if (ret)
+ goto err_power_down;
+ ov5693->streaming = !!enable;
/* power_off() here after streaming for regular PCs. */
if (!enable)
- power_down(sd);
-
-out:
- mutex_unlock(&dev->lock);
+ pm_runtime_put(&ov5693->client->dev);
+ return 0;
+err_power_down:
+ pm_runtime_put_noidle(&ov5693->client->dev);
return ret;
}
@@ -1517,7 +1618,7 @@ static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
int ret = 0;
mutex_lock(&ov5693->lock);
- ret = power_up(sd);
+ ret = ov5693_sensor_powerup(ov5693);
if (ret) {
dev_err(&client->dev, "ov5693 power-up err.\n");
goto fail_power_on;
@@ -1536,17 +1637,14 @@ static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
ov5693->otp_data = ov5693_otp_read(sd);
/* turn off sensor, after probed */
- ret = power_down(sd);
- if (ret) {
- dev_err(&client->dev, "ov5693 power-off err.\n");
- goto fail_power_on;
- }
+ ov5693_sensor_powerdown(ov5693);
+
mutex_unlock(&ov5693->lock);
return ret;
fail_power_on:
- power_down(sd);
+ ov5693_sensor_powerdown(ov5693);
dev_err(&client->dev, "sensor power-gating failed\n");
mutex_unlock(&ov5693->lock);
return ret;
@@ -1830,6 +1928,9 @@ static int ov5693_probe(struct i2c_client *client)
if (ret)
ov5693_remove(client);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
ret = v4l2_async_register_subdev_sensor_common(&ov5693->sd);
if (ret) {
dev_err(&client->dev, "failed to register V4L2 subdev: %d", ret);
@@ -1839,6 +1940,7 @@ static int ov5693_probe(struct i2c_client *client)
return ret;
media_entity_cleanup:
+ pm_runtime_disable(&client->dev);
media_entity_cleanup(&ov5693->sd.entity);
out_put_reset:
gpiod_put(ov5693->reset);
@@ -1848,6 +1950,10 @@ static int ov5693_probe(struct i2c_client *client)
return ret;
}
+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"},
{},
@@ -1858,6 +1964,7 @@ 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,
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 70ccb3aae4c7..b78d3b474a43 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -256,6 +256,7 @@ struct ov5693_device {
/* Current mode */
const struct ov5693_resolution *mode;
+ bool streaming;
struct camera_sensor_platform_data *platform_data;
ktime_t timestamp_t_focus_abs;
--
2.31.1
From 1051ec31e21f7f0750fee0462b11e449d940a2ec Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 28 Jan 2021 12:07:36 +0000
Subject: [PATCH] media: i2c: Remove old power methods from ov5693
Now that we have replaced the power_up/down() methods with a unified
function and pm_runtime support, we can remove these old methods from the
driver entirely along with some macros and a header.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 114 -------------------------------------
1 file changed, 114 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index f2eaa5f71a31..ce26ce86fbd5 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -27,7 +27,6 @@
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/mm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -40,27 +39,6 @@
#include "ov5693.h"
#include "ad5823.h"
-#define __cci_delay(t) \
- do { \
- if ((t) < 10) { \
- usleep_range((t) * 1000, ((t) + 1) * 1000); \
- } else { \
- msleep((t)); \
- } \
- } while (0)
-
-/* Value 30ms reached through experimentation on byt ecs.
- * The DS specifies a much lower value but when using a smaller value
- * the I2C bus sometimes locks up permanently when starting the camera.
- * This issue could not be reproduced on cht, so we can reduce the
- * delay value to a lower value when insmod.
- */
-static uint up_delay = 30;
-module_param(up_delay, uint, 0644);
-MODULE_PARM_DESC(up_delay,
- "Delay prior to the first CCI transaction for ov5693");
-
-
/* Exposure/gain */
#define OV5693_EXPOSURE_CTRL_HH_REG 0x3500
@@ -1197,93 +1175,6 @@ static int __maybe_unused ov5693_sensor_resume(struct device *dev)
return ret;
}
-static int __power_up(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- int ret;
-
- ret = clk_prepare_enable(ov5693->clk);
- if (ret) {
- dev_err(&client->dev, "Error enabling clock\n");
- return -EINVAL;
- }
-
- if (ov5693->indicator_led)
- gpiod_set_value_cansleep(ov5693->indicator_led, 1);
-
- ret = regulator_bulk_enable(OV5693_NUM_SUPPLIES,
- ov5693->supplies);
- if (ret)
- goto fail_power;
-
- gpiod_set_value_cansleep(ov5693->reset, 0);
-
- __cci_delay(up_delay);
-
- return 0;
-
-fail_power:
- if (ov5693->indicator_led)
- gpiod_set_value_cansleep(ov5693->indicator_led, 0);
- dev_err(&client->dev, "sensor power-up failed\n");
-
- return ret;
-}
-
-static int power_down(struct v4l2_subdev *sd)
-{
- struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
-
- ov5693->focus = OV5693_INVALID_CONFIG;
-
- gpiod_set_value_cansleep(ov5693->reset, 1);
- gpiod_set_value_cansleep(ov5693->powerdown, 1);
-
- clk_disable_unprepare(ov5693->clk);
-
- if (ov5693->indicator_led)
- gpiod_set_value_cansleep(ov5693->indicator_led, 0);
- return regulator_bulk_disable(OV5693_NUM_SUPPLIES, ov5693->supplies);
-}
-
-static int power_up(struct v4l2_subdev *sd)
-{
- static const int retry_count = 4;
- int i, ret;
-
- for (i = 0; i < retry_count; i++) {
- ret = __power_up(sd);
- if (!ret)
- return 0;
-
- power_down(sd);
- }
- return ret;
-}
-
-static int ov5693_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- dev_info(&client->dev, "%s: on %d\n", __func__, on);
-
- if (on == 0)
- return power_down(sd);
-
- /* on == 1 */
- ret = power_up(sd);
- if (!ret) {
- ret = ov5693_init(sd);
- /* restore settings */
- ov5693_res = ov5693_res_video;
- N_RES = N_RES_VIDEO;
- }
-
- return ret;
-}
-
/*
* distance - calculate the distance
* @res: resolution
@@ -1694,10 +1585,6 @@ static const struct v4l2_subdev_video_ops ov5693_video_ops = {
.g_frame_interval = ov5693_g_frame_interval,
};
-static const struct v4l2_subdev_core_ops ov5693_core_ops = {
- .s_power = ov5693_s_power,
-};
-
static const struct v4l2_subdev_pad_ops ov5693_pad_ops = {
.enum_mbus_code = ov5693_enum_mbus_code,
.enum_frame_size = ov5693_enum_frame_size,
@@ -1707,7 +1594,6 @@ static const struct v4l2_subdev_pad_ops ov5693_pad_ops = {
};
static const struct v4l2_subdev_ops ov5693_ops = {
- .core = &ov5693_core_ops,
.video = &ov5693_video_ops,
.pad = &ov5693_pad_ops,
};
--
2.31.1
From 51ed4c0166e8a5775506db1f50420516e31f7e0f Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Thu, 28 Jan 2021 12:14:00 +0000
Subject: [PATCH] media: i2c: Trim unused headers from ov5693
The ov5693 driver includes a ton of unecessary headers,
trim the list down.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index ce26ce86fbd5..b3b391a49fdb 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -20,19 +20,11 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/errno.h>
#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
#include <linux/module.h>
-#include <linux/mm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/types.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
--
2.31.1
From 271f6c470f8922faf28575b68345a99edb430799 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 13 Feb 2021 21:39:35 +0000
Subject: [PATCH] media: i2c: Remove VCM stuff
This all needs binning, since we have no idea if it's right. It needs to
be moved to a driver for the VCM device I guess.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 325 +------------------------------------
1 file changed, 1 insertion(+), 324 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index b3b391a49fdb..2c82b6578de9 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -76,72 +76,6 @@
#define OV5693_PIXEL_ARRAY_WIDTH 2592U
#define OV5693_PIXEL_ARRAY_HEIGHT 1944U
-static int vcm_ad_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
-{
- int err;
- struct i2c_msg msg;
- u8 buf[2];
-
- buf[0] = reg;
- buf[1] = val;
-
- msg.addr = VCM_ADDR;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = &buf[0];
-
- err = i2c_transfer(client->adapter, &msg, 1);
- if (err != 1) {
- dev_err(&client->dev, "%s: vcm i2c fail, err code = %d\n",
- __func__, err);
- return -EIO;
- }
- return 0;
-}
-
-static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val)
-{
- struct i2c_msg msg;
- u8 buf[2];
-
- buf[0] = reg;
- buf[1] = val;
- msg.addr = AD5823_VCM_ADDR;
- msg.flags = 0;
- msg.len = 0x02;
- msg.buf = &buf[0];
-
- if (i2c_transfer(client->adapter, &msg, 1) != 1)
- return -EIO;
- return 0;
-}
-
-static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
-{
- struct i2c_msg msg[2];
- u8 buf[2];
-
- buf[0] = reg;
- buf[1] = 0;
-
- msg[0].addr = AD5823_VCM_ADDR;
- msg[0].flags = 0;
- msg[0].len = 0x01;
- msg[0].buf = &buf[0];
-
- msg[1].addr = 0x0c;
- msg[1].flags = I2C_M_RD;
- msg[1].len = 0x01;
- msg[1].buf = &buf[1];
- *val = 0;
- if (i2c_transfer(client->adapter, msg, 2) != 2)
- return -EIO;
- *val = buf[1];
- return 0;
-}
-
-static const u32 ov5693_embedded_effective_size = 28;
-
/* i2c read/write stuff */
static int ov5693_read_reg(struct i2c_client *client,
u16 data_length, u16 reg, u16 *val)
@@ -215,69 +149,6 @@ static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data)
return ret == num_msg ? 0 : -EIO;
}
-static int vcm_dw_i2c_write(struct i2c_client *client, u16 data)
-{
- struct i2c_msg msg;
- const int num_msg = 1;
- int ret;
- __be16 val;
-
- val = cpu_to_be16(data);
- msg.addr = VCM_ADDR;
- msg.flags = 0;
- msg.len = OV5693_16BIT;
- msg.buf = (void *)&val;
-
- ret = i2c_transfer(client->adapter, &msg, 1);
-
- return ret == num_msg ? 0 : -EIO;
-}
-
-/*
- * Theory: per datasheet, the two VCMs both allow for a 2-byte read.
- * The DW9714 doesn't actually specify what this does (it has a
- * two-byte write-only protocol, but specifies the read sequence as
- * legal), but it returns the same data (zeroes) always, after an
- * undocumented initial NAK. The AD5823 has a one-byte address
- * register to which all writes go, and subsequent reads will cycle
- * through the 8 bytes of registers. Notably, the default values (the
- * device is always power-cycled affirmatively, so we can rely on
- * these) in AD5823 are not pairwise repetitions of the same 16 bit
- * word. So all we have to do is sequentially read two bytes at a
- * time and see if we detect a difference in any of the first four
- * pairs.
- */
-static int vcm_detect(struct i2c_client *client)
-{
- int i, ret;
- struct i2c_msg msg;
- u16 data0 = 0, data;
-
- for (i = 0; i < 4; i++) {
- msg.addr = VCM_ADDR;
- msg.flags = I2C_M_RD;
- msg.len = sizeof(data);
- msg.buf = (u8 *)&data;
- ret = i2c_transfer(client->adapter, &msg, 1);
-
- /*
- * DW9714 always fails the first read and returns
- * zeroes for subsequent ones
- */
- if (i == 0 && ret == -EREMOTEIO) {
- data0 = 0;
- continue;
- }
-
- if (i == 0)
- data0 = data;
-
- if (data != data0)
- return VCM_AD5823;
- }
- return ret == 1 ? VCM_DW9714 : ret;
-}
-
static int ov5693_write_reg(struct i2c_client *client, u16 data_length,
u16 reg, u16 val)
{
@@ -654,89 +525,6 @@ static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value)
return ret;
}
-static int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = -EINVAL;
- u8 vcm_code;
-
- ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code);
- if (ret)
- return ret;
-
- /* set reg VCM_CODE_MSB Bit[1:0] */
- vcm_code = (vcm_code & VCM_CODE_MSB_MASK) |
- ((val >> 8) & ~VCM_CODE_MSB_MASK);
- ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code);
- if (ret)
- return ret;
-
- /* set reg VCM_CODE_LSB Bit[7:0] */
- ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, (val & 0xff));
- if (ret)
- return ret;
-
- /* set required vcm move time */
- vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF
- - AD5823_HIGH_FREQ_RANGE;
- ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code);
-
- return ret;
-}
-
-static int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value)
-{
- value = min(value, AD5823_MAX_FOCUS_POS);
- return ad5823_t_focus_vcm(sd, value);
-}
-
-static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value)
-{
- struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = 0;
-
- dev_dbg(&client->dev, "%s: FOCUS_POS: 0x%x\n", __func__, value);
- value = clamp(value, 0, OV5693_VCM_MAX_FOCUS_POS);
- if (ov5693->vcm == VCM_DW9714) {
- if (ov5693->vcm_update) {
- ret = vcm_dw_i2c_write(client, VCM_PROTECTION_OFF);
- if (ret)
- return ret;
- ret = vcm_dw_i2c_write(client, DIRECT_VCM);
- if (ret)
- return ret;
- ret = vcm_dw_i2c_write(client, VCM_PROTECTION_ON);
- if (ret)
- return ret;
- ov5693->vcm_update = false;
- }
- ret = vcm_dw_i2c_write(client,
- vcm_val(value, VCM_DEFAULT_S));
- } else if (ov5693->vcm == VCM_AD5823) {
- ad5823_t_focus_abs(sd, value);
- }
- if (ret == 0) {
- ov5693->number_of_steps = value - ov5693->focus;
- ov5693->focus = value;
- ov5693->timestamp_t_focus_abs = ktime_get();
- } else
- dev_err(&client->dev,
- "%s: i2c failed. ret %d\n", __func__, ret);
-
- return ret;
-}
-
-static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value)
-{
- struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
-
- return ov5693_t_focus_abs(sd, ov5693->focus + value);
-}
-
-#define DELAY_PER_STEP_NS 1000000
-#define DELAY_MAX_PER_STEP_NS (1000000 * 1023)
-
/* Exposure */
static int ov5693_get_exposure(struct ov5693_device *ov5693)
@@ -911,16 +699,6 @@ static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
switch (ctrl->id) {
- case V4L2_CID_FOCUS_ABSOLUTE:
- dev_dbg(&client->dev, "%s: CID_FOCUS_ABSOLUTE:%d.\n",
- __func__, ctrl->val);
- ret = ov5693_t_focus_abs(&ov5693->sd, ctrl->val);
- break;
- case V4L2_CID_FOCUS_RELATIVE:
- dev_dbg(&client->dev, "%s: CID_FOCUS_RELATIVE:%d.\n",
- __func__, ctrl->val);
- ret = ov5693_t_focus_rel(&ov5693->sd, ctrl->val);
- break;
case V4L2_CID_EXPOSURE:
dev_dbg(&client->dev, "%s: CID_EXPOSURE:%d.\n",
__func__, ctrl->val);
@@ -983,90 +761,6 @@ static const struct v4l2_ctrl_ops ov5693_ctrl_ops = {
.g_volatile_ctrl = ov5693_g_volatile_ctrl
};
-static const struct v4l2_ctrl_config ov5693_controls[] = {
- {
- .ops = &ov5693_ctrl_ops,
- .id = V4L2_CID_FOCUS_ABSOLUTE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "focus move absolute",
- .min = 0,
- .max = OV5693_VCM_MAX_FOCUS_POS,
- .step = 1,
- .def = 0,
- .flags = 0,
- },
- {
- .ops = &ov5693_ctrl_ops,
- .id = V4L2_CID_FOCUS_RELATIVE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "focus move relative",
- .min = OV5693_VCM_MAX_FOCUS_NEG,
- .max = OV5693_VCM_MAX_FOCUS_POS,
- .step = 1,
- .def = 0,
- .flags = 0,
- },
-};
-
-static int ov5693_isp_configure(struct ov5693_device *ov5693)
-{
- int ret;
-
- /* Enable lens correction. */
- ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
- OV5693_ISP_CTRL0_REG, 0x86);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int ov5693_init(struct v4l2_subdev *sd)
-{
- struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- if (!ov5693->has_vcm)
- return 0;
-
- dev_info(&client->dev, "%s\n", __func__);
- mutex_lock(&ov5693->lock);
- ov5693->vcm_update = false;
-
- if (ov5693->vcm == VCM_AD5823) {
- ret = vcm_ad_i2c_wr8(client, 0x01, 0x01); /* vcm init test */
- if (ret)
- dev_err(&client->dev,
- "vcm reset failed\n");
- /*change the mode*/
- ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB,
- AD5823_RING_CTRL_ENABLE);
- if (ret)
- dev_err(&client->dev,
- "vcm enable ringing failed\n");
- ret = ad5823_i2c_write(client, AD5823_REG_MODE,
- AD5823_ARC_RES1);
- if (ret)
- dev_err(&client->dev,
- "vcm change mode failed\n");
- }
-
- /*change initial focus value for ad5823*/
- if (ov5693->vcm == VCM_AD5823) {
- ov5693->focus = AD5823_INIT_FOCUS_POS;
- ov5693_t_focus_abs(sd, AD5823_INIT_FOCUS_POS);
- } else {
- ov5693->focus = 0;
- ov5693_t_focus_abs(sd, 0);
- }
-
- ov5693_isp_configure(ov5693);
- mutex_unlock(&ov5693->lock);
-
- return 0;
-}
-
static int ov5693_sw_standby(struct ov5693_device *ov5693, bool standby)
{
return ov5693_write_reg(ov5693->client, OV5693_8BIT, OV5693_SW_STREAM,
@@ -1327,9 +1021,6 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
continue;
}
- mutex_unlock(&ov5693->lock);
- ov5693_init(sd);
- mutex_lock(&ov5693->lock);
ret = startup(sd);
if (ret)
dev_err(&client->dev, " startup() FAILED!\n");
@@ -1507,9 +1198,6 @@ static int ov5693_s_config(struct v4l2_subdev *sd, int irq)
goto fail_power_on;
}
- if (!ov5693->vcm)
- ov5693->vcm = vcm_detect(client);
-
/* config & detect sensor */
ret = ov5693_detect(client);
if (ret) {
@@ -1617,24 +1305,17 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
struct i2c_client *client = v4l2_get_subdevdata(&ov5693->sd);
const struct v4l2_ctrl_ops *ops = &ov5693_ctrl_ops;
struct v4l2_fwnode_device_properties props;
- unsigned int i;
int ret;
int hblank;
int vblank_max, vblank_min, vblank_def;
int exposure_max;
- ret = v4l2_ctrl_handler_init(&ov5693->ctrl_handler,
- ARRAY_SIZE(ov5693_controls));
+ ret = v4l2_ctrl_handler_init(&ov5693->ctrl_handler, 8);
if (ret) {
ov5693_remove(client);
return ret;
}
- for (i = 0; i < ARRAY_SIZE(ov5693_controls); i++)
- v4l2_ctrl_new_custom(&ov5693->ctrl_handler,
- &ov5693_controls[i],
- NULL);
-
/* link freq */
ov5693->ctrls.link_freq = v4l2_ctrl_new_int_menu(&ov5693->ctrl_handler,
NULL, V4L2_CID_LINK_FREQ,
@@ -1766,10 +1447,6 @@ static int ov5693_probe(struct i2c_client *client)
ov5693->client = client;
- /* check if VCM device exists */
- /* TODO: read from SSDB */
- ov5693->has_vcm = false;
-
mutex_init(&ov5693->lock);
v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops);
--
2.31.1
From 5208a3e8ef37658d2ddcfd687742eb2591a19530 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 13 Feb 2021 22:16:08 +0000
Subject: [PATCH] media: i2c: Tidy up ov5693 sensor init
The initialisation of a mode when the sensor is activated is a bit messy,
so lets tidy that up a bit to bring it in line with other drivers.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 100 ++++++++++++++++---------------------
1 file changed, 42 insertions(+), 58 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 2c82b6578de9..313bc9177328 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -767,6 +767,42 @@ static int ov5693_sw_standby(struct ov5693_device *ov5693, bool standby)
standby ? OV5693_STOP_STREAMING : OV5693_START_STREAMING);
}
+static int ov5693_sw_reset(struct ov5693_device *ov5693)
+{
+ return ov5693_write_reg(ov5693->client, OV5693_8BIT, OV5693_SW_RESET,
+ 0x01);
+}
+
+static int ov5693_sensor_init(struct ov5693_device *ov5693)
+{
+ struct i2c_client *client = ov5693->client;
+ int ret = 0;
+
+ ret = ov5693_sw_reset(ov5693);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 reset err.\n");
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(client, ov5693_global_setting);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 write register err.\n");
+ return ret;
+ }
+
+ ret = ov5693_write_reg_array(client, ov5693_res[ov5693->fmt_idx].regs);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 write register err.\n");
+ return ret;
+ }
+
+ ret = ov5693_sw_standby(ov5693, true);
+ if (ret)
+ dev_err(&client->dev, "ov5693 stream off error\n");
+
+ return ret;
+}
+
static void ov5693_sensor_powerdown(struct ov5693_device *ov5693)
{
gpiod_set_value_cansleep(ov5693->reset, 1);
@@ -846,6 +882,12 @@ static int __maybe_unused ov5693_sensor_resume(struct device *dev)
if (ret)
goto out_unlock;
+ ret = ov5693_sensor_init(ov5693);
+ if (ret) {
+ dev_err(&client->dev, "ov5693 sensor init failure\n");
+ goto err_power;
+ }
+
if (ov5693->streaming) {
ret = ov5693_sw_standby(ov5693, false);
if (ret)
@@ -944,35 +986,6 @@ static int get_resolution_index(int w, int h)
return -1;
}
-/* TODO: remove it. */
-static int startup(struct v4l2_subdev *sd)
-{
- struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = 0;
-
- ret = ov5693_write_reg(client, OV5693_8BIT,
- OV5693_SW_RESET, 0x01);
- if (ret) {
- dev_err(&client->dev, "ov5693 reset err.\n");
- return ret;
- }
-
- ret = ov5693_write_reg_array(client, ov5693_global_setting);
- if (ret) {
- dev_err(&client->dev, "ov5693 write register err.\n");
- return ret;
- }
-
- ret = ov5693_write_reg_array(client, ov5693_res[ov5693->fmt_idx].regs);
- if (ret) {
- dev_err(&client->dev, "ov5693 write register err.\n");
- return ret;
- }
-
- return ret;
-}
-
static int ov5693_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
@@ -982,7 +995,6 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
int idx;
- int cnt;
if (format->pad)
return -EINVAL;
@@ -1014,34 +1026,6 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
goto mutex_unlock;
}
- for (cnt = 0; cnt < OV5693_POWER_UP_RETRY_NUM; cnt++) {
- ret = ov5693_sensor_powerup(ov5693);
- if (ret) {
- dev_err(&client->dev, "power up failed\n");
- continue;
- }
-
- ret = startup(sd);
- if (ret)
- dev_err(&client->dev, " startup() FAILED!\n");
- else
- break;
- }
- if (cnt == OV5693_POWER_UP_RETRY_NUM) {
- dev_err(&client->dev, "power up failed, gave up\n");
- goto mutex_unlock;
- }
-
- /*
- * After sensor settings are set to HW, sometimes stream is started.
- * This would cause ISP timeout because ISP is not ready to receive
- * data yet. So add stop streaming here.
- */
- ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM,
- OV5693_STOP_STREAMING);
- if (ret)
- dev_warn(&client->dev, "ov5693 stream off err\n");
-
mutex_unlock:
mutex_unlock(&ov5693->lock);
return ret;
--
2.31.1
From 7e5080f1e226fb30cf78237a77c3d715d4eef081 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 12 Feb 2021 16:14:04 +0000
Subject: [PATCH] media: i2c: cleanup macros in ov5693.h
Lots of orphaned or duplicated macros in this header file. Clean
those up a bit so it's less ugly.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.h | 89 +-------------------------------------
1 file changed, 2 insertions(+), 87 deletions(-)
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index b78d3b474a43..6502777eb5f3 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -37,68 +37,23 @@
*/
#define ENABLE_NON_PREVIEW 1
-#define OV5693_POWER_UP_RETRY_NUM 5
-
/* Defines for register writes and register array processing */
-#define I2C_MSG_LENGTH 0x2
-#define I2C_RETRY_COUNT 5
-
-#define OV5693_FOCAL_LENGTH_NUM 334 /*3.34mm*/
-#define OV5693_FOCAL_LENGTH_DEM 100
-#define OV5693_F_NUMBER_DEFAULT_NUM 24
-#define OV5693_F_NUMBER_DEM 10
+#define I2C_MSG_LENGTH 0x2
#define MAX_FMTS 1
-/* sensor_mode_data read_mode adaptation */
-#define OV5693_READ_MODE_BINNING_ON 0x0400
-#define OV5693_READ_MODE_BINNING_OFF 0x00
-#define OV5693_INTEGRATION_TIME_MARGIN 8
-
-#define OV5693_MAX_EXPOSURE_VALUE 0xFFF1
-#define OV5693_MAX_GAIN_VALUE 0xFF
-
-/*
- * focal length bits definition:
- * bits 31-16: numerator, bits 15-0: denominator
- */
-#define OV5693_FOCAL_LENGTH_DEFAULT 0x1B70064
-
-/*
- * current f-number bits definition:
- * bits 31-16: numerator, bits 15-0: denominator
- */
-#define OV5693_F_NUMBER_DEFAULT 0x18000a
-
-/*
- * f-number range bits definition:
- * bits 31-24: max f-number numerator
- * bits 23-16: max f-number denominator
- * bits 15-8: min f-number numerator
- * bits 7-0: min f-number denominator
- */
-#define OV5693_F_NUMBER_RANGE 0x180a180a
#define OV5693_ID 0x5690
-#define OV5693_FINE_INTG_TIME_MIN 0
-#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0
-#define OV5693_COARSE_INTG_TIME_MIN 1
-#define OV5693_COARSE_INTG_TIME_MAX_MARGIN 6
-
-#define OV5693_BIN_FACTOR_MAX 4
/*
* OV5693 System control registers
*/
-#define OV5693_SW_SLEEP 0x0100
#define OV5693_SW_RESET 0x0103
#define OV5693_SW_STREAM 0x0100
#define OV5693_SC_CMMN_CHIP_ID_H 0x300A
#define OV5693_SC_CMMN_CHIP_ID_L 0x300B
-#define OV5693_SC_CMMN_SCCB_ID 0x300C
#define OV5693_SC_CMMN_SUB_ID 0x302A /* process, version*/
-/*Bit[7:4] Group control, Bit[3:0] Group ID*/
-#define OV5693_GROUP_ACCESS 0x3208
+
/*
*Bit[3:0] Bit[19:16] of exposure,
*remaining 16 bits lies in Reg0x3501&Reg0x3502
@@ -110,18 +65,6 @@
#define OV5693_AGC_H 0x350A
#define OV5693_AGC_L 0x350B /*Bit[7:0] of gain*/
-#define OV5693_HORIZONTAL_START_H 0x3800 /*Bit[11:8]*/
-#define OV5693_HORIZONTAL_START_L 0x3801 /*Bit[7:0]*/
-#define OV5693_VERTICAL_START_H 0x3802 /*Bit[11:8]*/
-#define OV5693_VERTICAL_START_L 0x3803 /*Bit[7:0]*/
-#define OV5693_HORIZONTAL_END_H 0x3804 /*Bit[11:8]*/
-#define OV5693_HORIZONTAL_END_L 0x3805 /*Bit[7:0]*/
-#define OV5693_VERTICAL_END_H 0x3806 /*Bit[11:8]*/
-#define OV5693_VERTICAL_END_L 0x3807 /*Bit[7:0]*/
-#define OV5693_HORIZONTAL_OUTPUT_SIZE_H 0x3808 /*Bit[3:0]*/
-#define OV5693_HORIZONTAL_OUTPUT_SIZE_L 0x3809 /*Bit[7:0]*/
-#define OV5693_VERTICAL_OUTPUT_SIZE_H 0x380a /*Bit[3:0]*/
-#define OV5693_VERTICAL_OUTPUT_SIZE_L 0x380b /*Bit[7:0]*/
/*High 8-bit, and low 8-bit HTS address is 0x380d*/
#define OV5693_TIMING_HTS_H 0x380C
/*High 8-bit, and low 8-bit HTS address is 0x380d*/
@@ -141,34 +84,6 @@
#define OV5693_START_STREAMING 0x01
#define OV5693_STOP_STREAMING 0x00
-#define VCM_ADDR 0x0c
-#define VCM_CODE_MSB 0x04
-
-#define OV5693_INVALID_CONFIG 0xffffffff
-
-#define OV5693_VCM_SLEW_STEP 0x30F0
-#define OV5693_VCM_SLEW_STEP_MAX 0x7
-#define OV5693_VCM_SLEW_STEP_MASK 0x7
-#define OV5693_VCM_CODE 0x30F2
-#define OV5693_VCM_SLEW_TIME 0x30F4
-#define OV5693_VCM_SLEW_TIME_MAX 0xffff
-#define OV5693_VCM_ENABLE 0x8000
-
-#define OV5693_VCM_MAX_FOCUS_NEG -1023
-#define OV5693_VCM_MAX_FOCUS_POS 1023
-
-#define DLC_ENABLE 1
-#define DLC_DISABLE 0
-#define VCM_PROTECTION_OFF 0xeca3
-#define VCM_PROTECTION_ON 0xdc51
-#define VCM_DEFAULT_S 0x0
-#define vcm_step_s(a) (u8)(a & 0xf)
-#define vcm_step_mclk(a) (u8)((a >> 4) & 0x3)
-#define vcm_dlc_mclk(dlc, mclk) (u16)((dlc << 3) | mclk | 0xa104)
-#define vcm_tsrc(tsrc) (u16)(tsrc << 3 | 0xf200)
-#define vcm_val(data, s) (u16)(data << 4 | s)
-#define DIRECT_VCM vcm_dlc_mclk(0, 0)
-
/* Defines for OTP Data Registers */
#define OV5693_FRAME_OFF_NUM 0x4202
#define OV5693_OTP_BYTE_MAX 32 //change to 32 as needed by otpdata
--
2.31.1
From 2e668cb83a45e959ca777e71f18b1f7eb3bdf804 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 12 Feb 2021 16:19:09 +0000
Subject: [PATCH] media: i2c: use devm_kzalloc() to initialise ov5693
There's a memory leak in probe because we're not using devres; swtich
so that we are.
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 313bc9177328..d092ed698eb3 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1425,7 +1425,7 @@ static int ov5693_probe(struct i2c_client *client)
dev_info(&client->dev, "%s() called", __func__);
- ov5693 = kzalloc(sizeof(*ov5693), GFP_KERNEL);
+ ov5693 = devm_kzalloc(&client->dev, sizeof(*ov5693), GFP_KERNEL);
if (!ov5693)
return -ENOMEM;
--
2.31.1
From 629fb46807053b881ff5edaf08b97106c07a38ad Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Fri, 12 Feb 2021 16:26:21 +0000
Subject: [PATCH] media: i2c: Check for supported clk rate in probe
The ov5693 driver is configured to support a 19.2MHz external clock only.
Check that we do indeed have that value and if not, exit with -EINVAL.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 8 ++++++++
drivers/media/i2c/ov5693.h | 2 ++
2 files changed, 10 insertions(+)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index d092ed698eb3..8082d37841da 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1421,6 +1421,7 @@ static int ov5693_get_regulators(struct ov5693_device *ov5693)
static int ov5693_probe(struct i2c_client *client)
{
struct ov5693_device *ov5693;
+ u32 clk_rate;
int ret = 0;
dev_info(&client->dev, "%s() called", __func__);
@@ -1441,6 +1442,13 @@ static int ov5693_probe(struct i2c_client *client)
return -EINVAL;
}
+ 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)
goto out_free;
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 6502777eb5f3..0dfbbe9a0ff2 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -100,6 +100,8 @@
#define OV5693_OTP_READ_ONETIME 16
#define OV5693_OTP_MODE_READ 1
+#define OV5693_XVCLK_FREQ 19200000
+
/* link freq and pixel rate required for IPU3 */
#define OV5693_LINK_FREQ_400MHZ 400000000
/* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample
--
2.31.1
From e2e5c6fed06e85872f36cd284023b5b3b26a6b04 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 13 Feb 2021 23:17:50 +0000
Subject: [PATCH] media: i2c: Use devres to fetch gpios
Use devres; it'll simplify error handling through this function
and probe.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 22 +++++-----------------
1 file changed, 5 insertions(+), 17 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 8082d37841da..c580159079d2 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1270,8 +1270,6 @@ static int ov5693_remove(struct i2c_client *client)
dev_info(&client->dev, "%s...\n", __func__);
- gpiod_put(ov5693->reset);
- gpiod_put(ov5693->indicator_led);
while (i--)
regulator_put(ov5693->supplies[i].consumer);
@@ -1372,38 +1370,28 @@ static int ov5693_init_controls(struct ov5693_device *ov5693)
static int ov5693_configure_gpios(struct ov5693_device *ov5693)
{
- int ret;
-
- ov5693->reset = gpiod_get_optional(&ov5693->client->dev, "reset",
+ ov5693->reset = devm_gpiod_get_optional(&ov5693->client->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(ov5693->reset)) {
dev_err(&ov5693->client->dev, "Couldn't find reset GPIO\n");
return PTR_ERR(ov5693->reset);
}
- ov5693->powerdown = gpiod_get_optional(&ov5693->client->dev, "powerdown",
+ ov5693->powerdown = devm_gpiod_get_optional(&ov5693->client->dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(ov5693->powerdown)) {
dev_err(&ov5693->client->dev, "Couldn't find powerdown GPIO\n");
- ret = PTR_ERR(ov5693->powerdown);
- goto err_put_reset;
+ return PTR_ERR(ov5693->powerdown);
}
- ov5693->indicator_led = gpiod_get_optional(&ov5693->client->dev, "indicator-led",
+ ov5693->indicator_led = devm_gpiod_get_optional(&ov5693->client->dev, "indicator-led",
GPIOD_OUT_HIGH);
if (IS_ERR(ov5693->indicator_led)) {
dev_err(&ov5693->client->dev, "Couldn't find indicator-led GPIO\n");
- ret = PTR_ERR(ov5693->indicator_led);
- goto err_put_powerdown;
+ return PTR_ERR(ov5693->indicator_led);
}
return 0;
-err_put_reset:
- gpiod_put(ov5693->reset);
-err_put_powerdown:
- gpiod_put(ov5693->powerdown);
-
- return ret;
}
static int ov5693_get_regulators(struct ov5693_device *ov5693)
--
2.31.1
From 8d6f23cc4022885c37727c32ab37879f915cfac9 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sat, 13 Feb 2021 23:20:47 +0000
Subject: [PATCH] media: i2c: Use devres to fetch regulators
As before, use devres to simplify error handling and driver removal
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index c580159079d2..9f61b470f8ba 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1266,13 +1266,9 @@ static int ov5693_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- unsigned int i = OV5693_NUM_SUPPLIES;
dev_info(&client->dev, "%s...\n", __func__);
- while (i--)
- regulator_put(ov5693->supplies[i].consumer);
-
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&ov5693->sd.entity);
@@ -1401,7 +1397,7 @@ static int ov5693_get_regulators(struct ov5693_device *ov5693)
for (i = 0; i < OV5693_NUM_SUPPLIES; i++)
ov5693->supplies[i].supply = ov5693_supply_names[i];
- return regulator_bulk_get(&ov5693->client->dev,
+ return devm_regulator_bulk_get(&ov5693->client->dev,
OV5693_NUM_SUPPLIES,
ov5693->supplies);
}
--
2.31.1
From eed01e22edce2bb5c1a299cb01501ca62fc9dfb9 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 14 Feb 2021 12:39:14 +0000
Subject: [PATCH] media: i2c: remove debug print
The exposure configure function has a debug print. It's working fine,
so bin it.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 37 -------------------------------------
1 file changed, 37 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 9f61b470f8ba..622a7ddf4063 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -526,41 +526,6 @@ static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value)
}
/* Exposure */
-
-static int ov5693_get_exposure(struct ov5693_device *ov5693)
-{
- u32 exposure = 0;
- u16 tmp;
- int ret = 0;
-
- /* get exposure */
- ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
- OV5693_EXPOSURE_L,
- &tmp);
- if (ret)
- return ret;
-
- exposure |= ((tmp >> 4) & 0b1111);
-
- ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
- OV5693_EXPOSURE_M,
- &tmp);
- if (ret)
- return ret;
-
- exposure |= (tmp << 4);
- ret = ov5693_read_reg(ov5693->client, OV5693_8BIT,
- OV5693_EXPOSURE_H,
- &tmp);
- if (ret)
- return ret;
-
- exposure |= (tmp << 12);
-
- printk("exposure set to: %u\n", exposure);
- return ret;
-}
-
static int ov5693_exposure_configure(struct ov5693_device *ov5693, u32 exposure)
{
int ret;
@@ -571,7 +536,6 @@ static int ov5693_exposure_configure(struct ov5693_device *ov5693, u32 exposure)
*/
exposure = exposure * 16;
- ov5693_get_exposure(ov5693);
ret = ov5693_write_reg(ov5693->client, OV5693_8BIT,
OV5693_EXPOSURE_CTRL_HH_REG, OV5693_EXPOSURE_CTRL_HH(exposure));
if (ret)
@@ -586,7 +550,6 @@ static int ov5693_exposure_configure(struct ov5693_device *ov5693, u32 exposure)
OV5693_EXPOSURE_CTRL_L_REG, OV5693_EXPOSURE_CTRL_L(exposure));
if (ret)
return ret;
- ov5693_get_exposure(ov5693);
return 0;
}
--
2.31.1
From d6345bec14d3bdd085e99d8275b33287347bc2b6 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 14 Feb 2021 14:32:50 +0000
Subject: [PATCH] media: i2c: Remove unused resolutions from ov5693
The list of resolutions in here is really unmaintanably long. For now just
bin all of the ones that are not part of ov5693_res_video, which is the
only array of resolutions in use anyway.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.h | 357 +------------------------------------
1 file changed, 1 insertion(+), 356 deletions(-)
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 0dfbbe9a0ff2..29e6735112da 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -474,34 +474,7 @@ static struct ov5693_reg const ov5693_global_setting[] = {
};
#if ENABLE_NON_PREVIEW
-/*
- * 654x496 30fps 17ms VBlanking 2lane 10Bit (Scaling)
- */
-static struct ov5693_reg const ov5693_654x496[] = {
- {OV5693_8BIT, 0x3501, 0x3d},
- {OV5693_8BIT, 0x3502, 0x00},
- {OV5693_8BIT, 0x3708, 0xe6},
- {OV5693_8BIT, 0x3709, 0xc7},
- {OV5693_8BIT, 0x3803, 0x00},
- {OV5693_8BIT, 0x3806, 0x07},
- {OV5693_8BIT, 0x3807, 0xa3},
- {OV5693_8BIT, 0x3808, 0x02},
- {OV5693_8BIT, 0x3809, 0x90},
- {OV5693_8BIT, 0x380a, 0x01},
- {OV5693_8BIT, 0x380b, 0xf0},
- {OV5693_8BIT, 0x380c, 0x0a},
- {OV5693_8BIT, 0x380d, 0x80},
- {OV5693_8BIT, 0x380e, 0x07},
- {OV5693_8BIT, 0x380f, 0xc0},
- {OV5693_8BIT, 0x3811, 0x08},
- {OV5693_8BIT, 0x3813, 0x02},
- {OV5693_8BIT, 0x3814, 0x31},
- {OV5693_8BIT, 0x3815, 0x31},
- {OV5693_8BIT, 0x3820, 0x04},
- {OV5693_8BIT, 0x3821, 0x1f},
- {OV5693_8BIT, 0x5002, 0x80},
- {OV5693_TOK_TERM, 0, 0}
-};
+
/*
* 1296x976 30fps 17ms VBlanking 2lane 10Bit (Scaling)
@@ -660,62 +633,10 @@ static struct ov5693_reg const ov5693_736x496[] = {
};
#endif
-/*
-static struct ov5693_reg const ov5693_736x496[] = {
- {OV5693_8BIT, 0x3501, 0x7b},
- {OV5693_8BIT, 0x3502, 0x00},
- {OV5693_8BIT, 0x3708, 0xe6},
- {OV5693_8BIT, 0x3709, 0xc3},
- {OV5693_8BIT, 0x3803, 0x00},
- {OV5693_8BIT, 0x3806, 0x07},
- {OV5693_8BIT, 0x3807, 0xa3},
- {OV5693_8BIT, 0x3808, 0x02},
- {OV5693_8BIT, 0x3809, 0xe0},
- {OV5693_8BIT, 0x380a, 0x01},
- {OV5693_8BIT, 0x380b, 0xf0},
- {OV5693_8BIT, 0x380c, 0x0d},
- {OV5693_8BIT, 0x380d, 0xb0},
- {OV5693_8BIT, 0x380e, 0x05},
- {OV5693_8BIT, 0x380f, 0xf2},
- {OV5693_8BIT, 0x3811, 0x08},
- {OV5693_8BIT, 0x3813, 0x02},
- {OV5693_8BIT, 0x3814, 0x31},
- {OV5693_8BIT, 0x3815, 0x31},
- {OV5693_8BIT, 0x3820, 0x01},
- {OV5693_8BIT, 0x3821, 0x1f},
- {OV5693_8BIT, 0x5002, 0x00},
- {OV5693_TOK_TERM, 0, 0}
-};
-*/
/*
* 976x556 30fps 8.8ms VBlanking 2lane 10Bit (Scaling)
*/
#if ENABLE_NON_PREVIEW
-static struct ov5693_reg const ov5693_976x556[] = {
- {OV5693_8BIT, 0x3501, 0x7b},
- {OV5693_8BIT, 0x3502, 0x00},
- {OV5693_8BIT, 0x3708, 0xe2},
- {OV5693_8BIT, 0x3709, 0xc3},
- {OV5693_8BIT, 0x3803, 0xf0},
- {OV5693_8BIT, 0x3806, 0x06},
- {OV5693_8BIT, 0x3807, 0xa7},
- {OV5693_8BIT, 0x3808, 0x03},
- {OV5693_8BIT, 0x3809, 0xd0},
- {OV5693_8BIT, 0x380a, 0x02},
- {OV5693_8BIT, 0x380b, 0x2C},
- {OV5693_8BIT, 0x380c, 0x0a},
- {OV5693_8BIT, 0x380d, 0x80},
- {OV5693_8BIT, 0x380e, 0x07},
- {OV5693_8BIT, 0x380f, 0xc0},
- {OV5693_8BIT, 0x3811, 0x10},
- {OV5693_8BIT, 0x3813, 0x02},
- {OV5693_8BIT, 0x3814, 0x11},
- {OV5693_8BIT, 0x3815, 0x11},
- {OV5693_8BIT, 0x3820, 0x00},
- {OV5693_8BIT, 0x3821, 0x1e},
- {OV5693_8BIT, 0x5002, 0x80},
- {OV5693_TOK_TERM, 0, 0}
-};
/*DS from 2624x1492*/
static struct ov5693_reg const ov5693_1296x736[] = {
@@ -782,40 +703,6 @@ static struct ov5693_reg const ov5693_1636p_30fps[] = {
};
#endif
-static struct ov5693_reg const ov5693_1616x1216_30fps[] = {
- {OV5693_8BIT, 0x3501, 0x7b},
- {OV5693_8BIT, 0x3502, 0x80},
- {OV5693_8BIT, 0x3708, 0xe2},
- {OV5693_8BIT, 0x3709, 0xc3},
- {OV5693_8BIT, 0x3800, 0x00}, /*{3800,3801} Array X start*/
- {OV5693_8BIT, 0x3801, 0x08}, /* 04 //{3800,3801} Array X start*/
- {OV5693_8BIT, 0x3802, 0x00}, /*{3802,3803} Array Y start*/
- {OV5693_8BIT, 0x3803, 0x04}, /* 00 //{3802,3803} Array Y start*/
- {OV5693_8BIT, 0x3804, 0x0a}, /*{3804,3805} Array X end*/
- {OV5693_8BIT, 0x3805, 0x37}, /* 3b //{3804,3805} Array X end*/
- {OV5693_8BIT, 0x3806, 0x07}, /*{3806,3807} Array Y end*/
- {OV5693_8BIT, 0x3807, 0x9f}, /* a3 //{3806,3807} Array Y end*/
- {OV5693_8BIT, 0x3808, 0x06}, /*{3808,3809} Final output H size*/
- {OV5693_8BIT, 0x3809, 0x50}, /*{3808,3809} Final output H size*/
- {OV5693_8BIT, 0x380a, 0x04}, /*{380a,380b} Final output V size*/
- {OV5693_8BIT, 0x380b, 0xc0}, /*{380a,380b} Final output V size*/
- {OV5693_8BIT, 0x380c, 0x0a}, /*{380c,380d} HTS*/
- {OV5693_8BIT, 0x380d, 0x80}, /*{380c,380d} HTS*/
- {OV5693_8BIT, 0x380e, 0x07}, /*{380e,380f} VTS*/
- {OV5693_8BIT, 0x380f, 0xc0}, /* bc //{380e,380f} VTS*/
- {OV5693_8BIT, 0x3810, 0x00}, /*{3810,3811} windowing X offset*/
- {OV5693_8BIT, 0x3811, 0x10}, /*{3810,3811} windowing X offset*/
- {OV5693_8BIT, 0x3812, 0x00}, /*{3812,3813} windowing Y offset*/
- {OV5693_8BIT, 0x3813, 0x06}, /*{3812,3813} windowing Y offset*/
- {OV5693_8BIT, 0x3814, 0x11}, /*X subsample control*/
- {OV5693_8BIT, 0x3815, 0x11}, /*Y subsample control*/
- {OV5693_8BIT, 0x3820, 0x00}, /*FLIP/Binnning control*/
- {OV5693_8BIT, 0x3821, 0x1e}, /*MIRROR control*/
- {OV5693_8BIT, 0x5002, 0x00},
- {OV5693_8BIT, 0x5041, 0x84},
- {OV5693_TOK_TERM, 0, 0}
-};
-
/*
* 1940x1096 30fps 8.8ms VBlanking 2lane 10bit (Scaling)
*/
@@ -878,37 +765,6 @@ static struct ov5693_reg const ov5693_2592x1456_30fps[] = {
};
#endif
-static struct ov5693_reg const ov5693_2576x1456_30fps[] = {
- {OV5693_8BIT, 0x3501, 0x7b},
- {OV5693_8BIT, 0x3502, 0x00},
- {OV5693_8BIT, 0x3708, 0xe2},
- {OV5693_8BIT, 0x3709, 0xc3},
- {OV5693_8BIT, 0x3800, 0x00},
- {OV5693_8BIT, 0x3801, 0x00},
- {OV5693_8BIT, 0x3802, 0x00},
- {OV5693_8BIT, 0x3803, 0xf0},
- {OV5693_8BIT, 0x3804, 0x0a},
- {OV5693_8BIT, 0x3805, 0x3f},
- {OV5693_8BIT, 0x3806, 0x06},
- {OV5693_8BIT, 0x3807, 0xa4},
- {OV5693_8BIT, 0x3808, 0x0a},
- {OV5693_8BIT, 0x3809, 0x10},
- {OV5693_8BIT, 0x380a, 0x05},
- {OV5693_8BIT, 0x380b, 0xb0},
- {OV5693_8BIT, 0x380c, 0x0a},
- {OV5693_8BIT, 0x380d, 0x80},
- {OV5693_8BIT, 0x380e, 0x07},
- {OV5693_8BIT, 0x380f, 0xc0},
- {OV5693_8BIT, 0x3811, 0x18},
- {OV5693_8BIT, 0x3813, 0x00},
- {OV5693_8BIT, 0x3814, 0x11},
- {OV5693_8BIT, 0x3815, 0x11},
- {OV5693_8BIT, 0x3820, 0x00},
- {OV5693_8BIT, 0x3821, 0x1e},
- {OV5693_8BIT, 0x5002, 0x00},
- {OV5693_TOK_TERM, 0, 0}
-};
-
/*
* 2592x1944 30fps 0.6ms VBlanking 2lane 10Bit
*/
@@ -940,49 +796,6 @@ static struct ov5693_reg const ov5693_2592x1944_30fps[] = {
};
#endif
-/*
- * 11:9 Full FOV Output, expected FOV Res: 2346x1920
- * ISP Effect Res: 1408x1152
- * Sensor out: 1424x1168, DS From: 2380x1952
- *
- * WA: Left Offset: 8, Hor scal: 64
- */
-#if ENABLE_NON_PREVIEW
-static struct ov5693_reg const ov5693_1424x1168_30fps[] = {
- {OV5693_8BIT, 0x3501, 0x3b}, /* long exposure[15:8] */
- {OV5693_8BIT, 0x3502, 0x80}, /* long exposure[7:0] */
- {OV5693_8BIT, 0x3708, 0xe2},
- {OV5693_8BIT, 0x3709, 0xc3},
- {OV5693_8BIT, 0x3800, 0x00}, /* TIMING_X_ADDR_START */
- {OV5693_8BIT, 0x3801, 0x50}, /* 80 */
- {OV5693_8BIT, 0x3802, 0x00}, /* TIMING_Y_ADDR_START */
- {OV5693_8BIT, 0x3803, 0x02}, /* 2 */
- {OV5693_8BIT, 0x3804, 0x09}, /* TIMING_X_ADDR_END */
- {OV5693_8BIT, 0x3805, 0xdd}, /* 2525 */
- {OV5693_8BIT, 0x3806, 0x07}, /* TIMING_Y_ADDR_END */
- {OV5693_8BIT, 0x3807, 0xa1}, /* 1953 */
- {OV5693_8BIT, 0x3808, 0x05}, /* TIMING_X_OUTPUT_SIZE */
- {OV5693_8BIT, 0x3809, 0x90}, /* 1424 */
- {OV5693_8BIT, 0x380a, 0x04}, /* TIMING_Y_OUTPUT_SIZE */
- {OV5693_8BIT, 0x380b, 0x90}, /* 1168 */
- {OV5693_8BIT, 0x380c, 0x0a}, /* TIMING_HTS */
- {OV5693_8BIT, 0x380d, 0x80},
- {OV5693_8BIT, 0x380e, 0x07}, /* TIMING_VTS */
- {OV5693_8BIT, 0x380f, 0xc0},
- {OV5693_8BIT, 0x3810, 0x00}, /* TIMING_ISP_X_WIN */
- {OV5693_8BIT, 0x3811, 0x02}, /* 2 */
- {OV5693_8BIT, 0x3812, 0x00}, /* TIMING_ISP_Y_WIN */
- {OV5693_8BIT, 0x3813, 0x00}, /* 0 */
- {OV5693_8BIT, 0x3814, 0x11}, /* TIME_X_INC */
- {OV5693_8BIT, 0x3815, 0x11}, /* TIME_Y_INC */
- {OV5693_8BIT, 0x3820, 0x00},
- {OV5693_8BIT, 0x3821, 0x1e},
- {OV5693_8BIT, 0x5002, 0x00},
- {OV5693_8BIT, 0x5041, 0x84}, /* scale is auto enabled */
- {OV5693_TOK_TERM, 0, 0}
-};
-#endif
-
/*
* 3:2 Full FOV Output, expected FOV Res: 2560x1706
* ISP Effect Res: 720x480
@@ -1022,173 +835,6 @@ static struct ov5693_reg const ov5693_736x496_30fps[] = {
{OV5693_TOK_TERM, 0, 0}
};
-static struct ov5693_reg const ov5693_2576x1936_30fps[] = {
- {OV5693_8BIT, 0x3501, 0x7b},
- {OV5693_8BIT, 0x3502, 0x00},
- {OV5693_8BIT, 0x3708, 0xe2},
- {OV5693_8BIT, 0x3709, 0xc3},
- {OV5693_8BIT, 0x3803, 0x00},
- {OV5693_8BIT, 0x3806, 0x07},
- {OV5693_8BIT, 0x3807, 0xa3},
- {OV5693_8BIT, 0x3808, 0x0a},
- {OV5693_8BIT, 0x3809, 0x10},
- {OV5693_8BIT, 0x380a, 0x07},
- {OV5693_8BIT, 0x380b, 0x90},
- {OV5693_8BIT, 0x380c, 0x0a},
- {OV5693_8BIT, 0x380d, 0x80},
- {OV5693_8BIT, 0x380e, 0x07},
- {OV5693_8BIT, 0x380f, 0xc0},
- {OV5693_8BIT, 0x3811, 0x18},
- {OV5693_8BIT, 0x3813, 0x00},
- {OV5693_8BIT, 0x3814, 0x11},
- {OV5693_8BIT, 0x3815, 0x11},
- {OV5693_8BIT, 0x3820, 0x00},
- {OV5693_8BIT, 0x3821, 0x1e},
- {OV5693_8BIT, 0x5002, 0x00},
- {OV5693_TOK_TERM, 0, 0}
-};
-
-static struct ov5693_resolution ov5693_res_preview[] = {
- {
- .desc = "ov5693_736x496_30fps",
- .width = 736,
- .height = 496,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_736x496_30fps,
- },
- {
- .desc = "ov5693_1616x1216_30fps",
- .width = 1616,
- .height = 1216,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_1616x1216_30fps,
- },
- {
- .desc = "ov5693_5M_30fps",
- .width = 2576,
- .height = 1456,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_2576x1456_30fps,
- },
- {
- .desc = "ov5693_5M_30fps",
- .width = 2576,
- .height = 1936,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_2576x1936_30fps,
- },
-};
-
-#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview))
-
-/*
- * Disable non-preview configurations until the configuration selection is
- * improved.
- */
-#if ENABLE_NON_PREVIEW
-struct ov5693_resolution ov5693_res_still[] = {
- {
- .desc = "ov5693_736x496_30fps",
- .width = 736,
- .height = 496,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_736x496_30fps,
- },
- {
- .desc = "ov5693_1424x1168_30fps",
- .width = 1424,
- .height = 1168,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_1424x1168_30fps,
- },
- {
- .desc = "ov5693_1616x1216_30fps",
- .width = 1616,
- .height = 1216,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_1616x1216_30fps,
- },
- {
- .desc = "ov5693_5M_30fps",
- .width = 2592,
- .height = 1456,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_2592x1456_30fps,
- },
- {
- .desc = "ov5693_5M_30fps",
- .width = 2592,
- .height = 1944,
- .pix_clk_freq = 160,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2688,
- .lines_per_frame = 1984,
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .regs = ov5693_2592x1944_30fps,
- },
-};
-
-#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still))
-
struct ov5693_resolution ov5693_res_video[] = {
{
.desc = "ov5693_736x496_30fps",
@@ -1343,4 +989,3 @@ struct ov5693_resolution ov5693_res_video[] = {
static struct ov5693_resolution *ov5693_res = ov5693_res_video;
static unsigned long N_RES = N_RES_VIDEO;
-#endif
--
2.31.1
From 5fd88253510a38efbc8d800a857fe93fbc15f94e Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 14 Feb 2021 14:45:58 +0000
Subject: [PATCH] media: i2c: update set_fmt() for ov5693
The set_fmt() function is a bit messy still, using home grown solutions to
find the closest supported resolution instead of the v4l2 helpers. It also
fails to update control ranges to account for the new mode (though this is
moot currently as they're all the same, but the probably shouldn't be).
Fix it up a little.
Signed-off-by: Daniel Scally <djrscally@gmail.com>
Patchset: cameras
---
drivers/media/i2c/ov5693.c | 148 ++++++++++---------------------------
drivers/media/i2c/ov5693.h | 5 +-
2 files changed, 40 insertions(+), 113 deletions(-)
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 622a7ddf4063..09c84006d5c9 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -753,7 +753,7 @@ static int ov5693_sensor_init(struct ov5693_device *ov5693)
return ret;
}
- ret = ov5693_write_reg_array(client, ov5693_res[ov5693->fmt_idx].regs);
+ ret = ov5693_write_reg_array(client, ov5693->mode->regs);
if (ret) {
dev_err(&client->dev, "ov5693 write register err.\n");
return ret;
@@ -866,128 +866,56 @@ static int __maybe_unused ov5693_sensor_resume(struct device *dev)
return ret;
}
-/*
- * distance - calculate the distance
- * @res: resolution
- * @w: width
- * @h: height
- *
- * Get the gap between res_w/res_h and w/h.
- * distance = (res_w/res_h - w/h) / (w/h) * 8192
- * res->width/height smaller than w/h wouldn't be considered.
- * The gap of ratio larger than 1/8 wouldn't be considered.
- * Returns the value of gap or -1 if fail.
- */
-#define LARGEST_ALLOWED_RATIO_MISMATCH 1024
-static int distance(struct ov5693_resolution *res, u32 w, u32 h)
-{
- int ratio;
- int distance;
-
- if (w == 0 || h == 0 ||
- res->width < w || res->height < h)
- return -1;
-
- ratio = res->width << 13;
- ratio /= w;
- ratio *= h;
- ratio /= res->height;
-
- distance = abs(ratio - 8192);
-
- if (distance > LARGEST_ALLOWED_RATIO_MISMATCH)
- return -1;
-
- return distance;
-}
-
-/* Return the nearest higher resolution index
- * Firstly try to find the approximate aspect ratio resolution
- * If we find multiple same AR resolutions, choose the
- * minimal size.
- */
-static int nearest_resolution_index(int w, int h)
-{
- int i;
- int idx = -1;
- int dist;
- int min_dist = INT_MAX;
- int min_res_w = INT_MAX;
- struct ov5693_resolution *tmp_res = NULL;
-
- for (i = 0; i < N_RES; i++) {
- tmp_res = &ov5693_res[i];
- dist = distance(tmp_res, w, h);
- if (dist == -1)
- continue;
- if (dist < min_dist) {
- min_dist = dist;
- idx = i;
- min_res_w = ov5693_res[i].width;
- continue;
- }
- if (dist == min_dist && ov5693_res[i].width < min_res_w)
- idx = i;
- }
-
- return idx;
-}
-
-static int get_resolution_index(int w, int h)
-{
- int i;
-
- for (i = 0; i < N_RES; i++) {
- if (w != ov5693_res[i].width)
- continue;
- if (h != ov5693_res[i].height)
- continue;
-
- return i;
- }
-
- return -1;
-}
-
static int ov5693_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
- struct v4l2_mbus_framefmt *fmt = &format->format;
struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const struct ov5693_resolution *mode;
+ int exposure_max;
int ret = 0;
- int idx;
+ int hblank;
if (format->pad)
return -EINVAL;
- if (!fmt)
- return -EINVAL;
mutex_lock(&ov5693->lock);
- idx = nearest_resolution_index(fmt->width, fmt->height);
- if (idx == -1) {
- /* return the largest resolution */
- fmt->width = ov5693_res[N_RES - 1].width;
- fmt->height = ov5693_res[N_RES - 1].height;
- } else {
- fmt->width = ov5693_res[idx].width;
- fmt->height = ov5693_res[idx].height;
- }
- fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ mode = v4l2_find_nearest_size(ov5693_res_video, ARRAY_SIZE(ov5693_res_video),
+ width, height, format->format.width,
+ format->format.height);
+
+ if (!mode)
+ return -EINVAL;
+
+ format->format.width = mode->width;
+ format->format.height = mode->height;
+ format->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- cfg->try_fmt = *fmt;
- ret = 0;
+ *v4l2_subdev_get_try_format(sd, cfg, format->pad) = format->format;
goto mutex_unlock;
}
- ov5693->fmt_idx = get_resolution_index(fmt->width, fmt->height);
- if (ov5693->fmt_idx == -1) {
- dev_err(&client->dev, "get resolution fail\n");
- ret = -EINVAL;
- goto mutex_unlock;
- }
+ ov5693->mode = mode;
+
+ /* Update limits and set FPS to default */
+ __v4l2_ctrl_modify_range(ov5693->ctrls.vblank,
+ mode->lines_per_frame - mode->height,
+ OV5693_TIMING_MAX_VTS - mode->height,
+ 1, mode->lines_per_frame - mode->height);
+ __v4l2_ctrl_s_ctrl(ov5693->ctrls.vblank,
+ mode->lines_per_frame - mode->height);
+
+ hblank = mode->pixels_per_line - mode->width;
+ __v4l2_ctrl_modify_range(ov5693->ctrls.hblank, hblank, hblank, 1, hblank);
+
+ exposure_max = mode->lines_per_frame - 8;
+ __v4l2_ctrl_modify_range(ov5693->ctrls.exposure,
+ ov5693->ctrls.exposure->minimum, exposure_max,
+ ov5693->ctrls.exposure->step,
+ ov5693->ctrls.exposure->val < exposure_max ?
+ ov5693->ctrls.exposure->val : exposure_max);
mutex_unlock:
mutex_unlock(&ov5693->lock);
@@ -1056,8 +984,8 @@ static int ov5693_get_fmt(struct v4l2_subdev *sd,
if (!fmt)
return -EINVAL;
- fmt->width = ov5693_res[ov5693->fmt_idx].width;
- fmt->height = ov5693_res[ov5693->fmt_idx].height;
+ fmt->width = ov5693->mode->width;
+ fmt->height = ov5693->mode->height;
fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
return 0;
@@ -1174,7 +1102,7 @@ static int ov5693_g_frame_interval(struct v4l2_subdev *sd,
struct ov5693_device *ov5693 = to_ov5693_sensor(sd);
interval->interval.numerator = 1;
- interval->interval.denominator = ov5693_res[ov5693->fmt_idx].fps;
+ interval->interval.denominator = ov5693->mode->fps;
return 0;
}
diff --git a/drivers/media/i2c/ov5693.h b/drivers/media/i2c/ov5693.h
index 29e6735112da..0377853f8b2b 100644
--- a/drivers/media/i2c/ov5693.h
+++ b/drivers/media/i2c/ov5693.h
@@ -127,8 +127,8 @@ struct ov5693_resolution {
u8 *desc;
const struct ov5693_reg *regs;
int res;
- int width;
- int height;
+ u32 width;
+ u32 height;
int fps;
int pix_clk_freq;
u16 pixels_per_line;
@@ -178,7 +178,6 @@ struct ov5693_device {
struct camera_sensor_platform_data *platform_data;
ktime_t timestamp_t_focus_abs;
int vt_pix_clk_freq_mhz;
- int fmt_idx;
int run_mode;
int otp_size;
u8 *otp_data;
--
2.31.1