* [PATCH v4 00/10] Add device tree support for Exynos4 SoC camera subsystem
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This series adds devicetree support for the front and rear camera of
the Exynos4412 SoC Trats2 board. It converts related drivers to use
the v4l2-async API. The SoC output clocks are provided to external image
image sensors through the common clock API.
I'd appreciate a DT binding maintainer reviewed patches 2/11, 3/11.
With an Ack I could finally push these things upstream.
Sylwester Nawrocki (10):
Documentation: dt: Add DT binding documentation for S5K6A3 image
sensor
Documentation: dt: Add DT binding documentation for S5C73M3 camera
Documentation: devicetree: Update Samsung FIMC DT binding
V4L: Add driver for s5k6a3 image sensor
V4L: s5c73m3: Add device tree support
exynos4-is: Use external s5k6a3 sensor driver
exynos4-is: Add clock provider for the SCLK_CAM clock outputs
exynos4-is: Add support for asynchronous subdevices registration
ARM: dts: Add rear camera nodes for Exynos4412 TRATS2 board
ARM: dts: exynos4: Update clk provider part of the camera subsystem
.../devicetree/bindings/media/samsung-fimc.txt | 36 +-
.../devicetree/bindings/media/samsung-s5c73m3.txt | 97 +++++
.../devicetree/bindings/media/samsung-s5k6a3.txt | 33 ++
arch/arm/boot/dts/exynos4.dtsi | 6 +-
arch/arm/boot/dts/exynos4412-trats2.dts | 86 ++++-
drivers/media/i2c/Kconfig | 8 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/s5c73m3/s5c73m3-core.c | 207 ++++++++---
drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 +
drivers/media/i2c/s5c73m3/s5c73m3.h | 4 +
drivers/media/i2c/s5k6a3.c | 388 ++++++++++++++++++++
drivers/media/platform/exynos4-is/fimc-is-regs.c | 2 +-
drivers/media/platform/exynos4-is/fimc-is-sensor.c | 285 +-------------
drivers/media/platform/exynos4-is/fimc-is-sensor.h | 49 +--
drivers/media/platform/exynos4-is/fimc-is.c | 97 ++---
drivers/media/platform/exynos4-is/fimc-is.h | 4 +-
drivers/media/platform/exynos4-is/media-dev.c | 329 ++++++++++++-----
drivers/media/platform/exynos4-is/media-dev.h | 32 +-
18 files changed, 1127 insertions(+), 543 deletions(-)
create mode 100644 Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
create mode 100644 Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
create mode 100644 drivers/media/i2c/s5k6a3.c
--
1.7.9.5
^ permalink raw reply
* [PATCH v4 01/10] Documentation: dt: Add DT binding documentation for S5K6A3 image sensor
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This patch adds binding documentation for the Samsung S5K6A3(YX)
raw image sensor.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
Changes since v3:
-none.
Changes since v2:
- rephrased 'clocks' and 'clock-names' properties' description;
---
.../devicetree/bindings/media/samsung-s5k6a3.txt | 33 ++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
diff --git a/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt b/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
new file mode 100644
index 0000000..cce01e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/samsung-s5k6a3.txt
@@ -0,0 +1,33 @@
+Samsung S5K6A3(YX) raw image sensor
+---------------------------------
+
+S5K6A3(YX) is a raw image sensor with MIPI CSI-2 and CCP2 image data interfaces
+and CCI (I2C compatible) control bus.
+
+Required properties:
+
+- compatible : "samsung,s5k6a3";
+- reg : I2C slave address of the sensor;
+- svdda-supply : core voltage supply;
+- svddio-supply : I/O voltage supply;
+- afvdd-supply : AF (actuator) voltage supply;
+- gpios : specifier of a GPIO connected to the RESET pin;
+- clocks : should contain list of phandle and clock specifier pairs
+ according to common clock bindings for the clocks described
+ in the clock-names property;
+- clock-names : should contain "extclk" entry for the sensor's EXTCLK clock;
+
+Optional properties:
+
+- clock-frequency : the frequency at which the "extclk" clock should be
+ configured to operate, in Hz; if this property is not
+ specified default 24 MHz value will be used.
+
+The common video interfaces bindings (see video-interfaces.txt) should be
+used to specify link to the image data receiver. The S5K6A3(YX) device
+node should contain one 'port' child node with an 'endpoint' subnode.
+
+Following properties are valid for the endpoint node:
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+ video-interfaces.txt. The sensor supports only one data lane.
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 02/10] Documentation: dt: Add DT binding documentation for S5C73M3 camera
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This adds DT binding documentation for Samsung S5C73M3 camera sensor
with an embedded ISP.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
Changes since v3:
- DT binding documentation separated into this patch;
Changes since v2:
- rephrased 'clocks' and 'clock-names' properties' description;
---
.../devicetree/bindings/media/samsung-s5c73m3.txt | 97 ++++++++++++++++++++
1 file changed, 97 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
diff --git a/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
new file mode 100644
index 0000000..4dd3776
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt
@@ -0,0 +1,97 @@
+Samsung S5C73M3 8Mp camera ISP
+------------------------------
+
+The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) video
+data busses. The I2C bus is the main control bus and additionally the SPI bus
+is used, mostly for transferring the firmware to and from the device. Two
+slave device nodes corresponding to these control bus interfaces are required
+and should be placed under respective bus controller nodes.
+
+I2C slave device node
+---------------------
+
+Required properties:
+
+- compatible : "samsung,s5c73m3";
+- reg : I2C slave address of the sensor;
+- vdd-int-supply : digital power supply (1.2V);
+- vdda-supply : analog power supply (1.2V);
+- vdd-reg-supply : regulator input power supply (2.8V);
+- vddio-host-supply : host I/O power supply (1.8V to 2.8V);
+- vddio-cis-supply : CIS I/O power supply (1.2V to 1.8V);
+- vdd-af-supply : lens power supply (2.8V);
+- xshutdown-gpios : specifier of GPIO connected to the XSHUTDOWN pin;
+- standby-gpios : specifier of GPIO connected to the STANDBY pin;
+- clocks : should contain list of phandle and clock specifier pairs
+ according to common clock bindings for the clocks described
+ in the clock-names property;
+- clock-names : should contain "cis_extclk" entry for the CIS_EXTCLK clock;
+
+Optional properties:
+
+- clock-frequency : the frequency at which the "cis_extclk" clock should be
+ configured to operate, in Hz; if this property is not
+ specified default 24 MHz value will be used.
+
+The common video interfaces bindings (see video-interfaces.txt) should be used
+to specify link from the S5C73M3 to an external image data receiver. The S5C73M3
+device node should contain one 'port' child node with an 'endpoint' subnode for
+this purpose. The data link from a raw image sensor to the S5C73M3 can be
+similarly specified, but it is optional since the S5C73M3 ISP and a raw image
+sensor are usually inseparable and form a hybrid module.
+
+Following properties are valid for the endpoint node(s):
+
+endpoint subnode
+----------------
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+ video-interfaces.txt. This sensor doesn't support data lane remapping
+ and physical lane indexes in subsequent elements of the array should
+ be only consecutive ascending values.
+
+SPI device node
+---------------
+
+Required properties:
+
+- compatible : "samsung,s5c73m3";
+
+For more details see description of the SPI busses bindings
+(../spi/spi-bus.txt) and bindings of a specific bus controller.
+
+Example:
+
+i2c@138A000000 {
+ ...
+ s5c73m3@3c {
+ compatible = "samsung,s5c73m3";
+ reg = <0x3c>;
+ vdd-int-supply = <&buck9_reg>;
+ vdda-supply = <&ldo17_reg>;
+ vdd-reg-supply = <&cam_io_reg>;
+ vddio-host-supply = <&ldo18_reg>;
+ vddio-cis-supply = <&ldo9_reg>;
+ vdd-af-supply = <&cam_af_reg>;
+ clock-frequency = <24000000>;
+ clocks = <&clk 0>;
+ clock-names = "cis_extclk";
+ reset-gpios = <&gpf1 3 1>;
+ standby-gpios = <&gpm0 1 1>;
+ port {
+ s5c73m3_ep: endpoint {
+ remote-endpoint = <&csis0_ep>;
+ data-lanes = <1 2 3 4>;
+ };
+ };
+ };
+};
+
+spi@1392000 {
+ ...
+ s5c73m3_spi: s5c73m3 {
+ compatible = "samsung,s5c73m3";
+ reg = <0>;
+ ...
+ };
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 03/10] Documentation: devicetree: Update Samsung FIMC DT binding
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This patch documents following updates of the Exynos4 SoC camera subsystem
devicetree binding:
- addition of #clock-cells property to 'camera' node - the #clock-cells
property is needed when the sensor sub-devices use clock provided by
the camera host interface;
- addition of an optional clock-output-names property;
- change of the clock-frequency at image sensor node from mandatory to
an optional property - there should be no need to require this property
by the camera host device binding, a default frequency value can ofen
be used;
- addition of a requirement of specific order of values in clocks/
clock-names properties, so the first two entry in the clock-names
property can be used as parent clock names for the camera master
clock provider. It happens all in-kernel dts files list the clock
in such order, thus there should be no regression as far as in-kernel
dts files are concerned.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
.../devicetree/bindings/media/samsung-fimc.txt | 36 +++++++++++++++-----
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 96312f6..1a5820d 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -20,6 +20,7 @@ Required properties:
the clock-names property;
- clock-names : must contain "sclk_cam0", "sclk_cam1", "pxl_async0",
"pxl_async1" entries, matching entries in the clocks property.
+ First two entries must be "sclk_cam0", "sclk_cam1".
The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used
to define a required pinctrl state named "default" and optional pinctrl states:
@@ -32,6 +33,22 @@ way around.
The 'camera' node must include at least one 'fimc' child node.
+Optional properties (*:
+
+- #clock-cells: from the common clock bindings (../clock/clock-bindings.txt),
+ must be 1. A clock provider is associated with the 'camera' node and it should
+ be referenced by external sensors that use clocks provided by the SoC on
+ CAM_*_CLKOUT pins. The clock specifier cell stores an index of a clock.
+ The indices are 0, 1 for CAM_A_CLKOUT, CAM_B_CLKOUT clocks respectively.
+
+- clock-output-names: from the common clock bindings, should contain names of
+ clocks registered by the camera subsystem corresponding to CAM_A_CLKOUT,
+ CAM_B_CLKOUT output clocks, in this order. Parent clock of these clocks are
+ specified be first two entries of the clock-names property.
+
+(* #clock-cells and clock-output-names are mandatory properties if external
+image sensor devices reference 'camera' device node as a clock provider.
+
'fimc' device nodes
-------------------
@@ -97,8 +114,8 @@ Image sensor nodes
The sensor device nodes should be added to their control bus controller (e.g.
I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
using the common video interfaces bindings, defined in video-interfaces.txt.
-The implementation of this bindings requires clock-frequency property to be
-present in the sensor device nodes.
+An optional clock-frequency property needs to be present in the sensor device
+nodes. Default value when this property is not present is 24 MHz.
Example:
@@ -114,7 +131,7 @@ Example:
vddio-supply = <...>;
clock-frequency = <24000000>;
- clocks = <...>;
+ clocks = <&camera 1>;
clock-names = "mclk";
port {
@@ -135,7 +152,7 @@ Example:
vddio-supply = <...>;
clock-frequency = <24000000>;
- clocks = <...>;
+ clocks = <&camera 0>;
clock-names = "mclk";
port {
@@ -149,12 +166,15 @@ Example:
camera {
compatible = "samsung,fimc", "simple-bus";
- #address-cells = <1>;
- #size-cells = <1>;
- status = "okay";
-
+ clocks = <&clock 132>, <&clock 133>;
+ clock-names = "sclk_cam0", "sclk_cam1";
+ #clock-cells = <1>;
+ clock-output-names = "cam_mclk_a", "cam_mclk_b";
pinctrl-names = "default";
pinctrl-0 = <&cam_port_a_clk_active>;
+ status = "okay";
+ #address-cells = <1>;
+ #size-cells = <1>;
/* parallel camera ports */
parallel-ports {
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 04/10] V4L: Add driver for s5k6a3 image sensor
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
galak-sgV2jX0FEOL9JmXXK+q4OQ,
kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, a.hajda-Sze3O3UU22JBDgjK7y7TUQ,
Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
This patch adds subdev driver for Samsung S5K6A3 raw image sensor.
As it is intended at the moment to be used only with the Exynos
FIMC-IS (camera ISP) subsystem it is pretty minimal subdev driver.
It doesn't do any I2C communication since the sensor is controlled
by the ISP and its own firmware.
This driver, if needed, can be updated in future into a regular
subdev driver where the main CPU communicates with the sensor
directly.
Signed-off-by: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
Changes since v3:
- clk_get() called moved from s_power() to driver probe() callback.
Changes since v1:
- added missing pm_runtime_disable(),
- removed subdev name overriding,
- s/S5K6A3_DEF_PIX/S5K6A3_DEFAULT_,
- merged patch adding v4l2-async API support,
- clock-frequency property is now optional and a default frequency
value will be used when it is missing, rather than bailing out,
- reset GPIO made mandatory as it is required for proper power
on/off sequences and all known boards use it,
- maximum image size is now 1412x1412 pixels, as specified in
in the S5K6A3YX datasheet.
- regulator_enable()/disable() used instead of the regulator bulk
API to ensure proper regulators enable/disable sequences.
Signed-off-by: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
drivers/media/i2c/Kconfig | 8 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/s5k6a3.c | 388 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 397 insertions(+)
create mode 100644 drivers/media/i2c/s5k6a3.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 4aa9c531..ac2d5b7 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
camera sensor with an embedded SoC image signal processor.
+config VIDEO_S5K6A3
+ tristate "Samsung S5K6A3 sensor support"
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6A3 raw
+ camera sensor.
+
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 48888ae..ab5aa34 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o
obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
new file mode 100644
index 0000000..d0b52cd
--- /dev/null
+++ b/drivers/media/i2c/s5k6a3.c
@@ -0,0 +1,388 @@
+/*
+ * Samsung S5K6A3 image sensor driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-subdev.h>
+
+#define S5K6A3_SENSOR_MAX_WIDTH 1412
+#define S5K6A3_SENSOR_MAX_HEIGHT 1412
+#define S5K6A3_SENSOR_MIN_WIDTH 32
+#define S5K6A3_SENSOR_MIN_HEIGHT 32
+
+#define S5K6A3_DEFAULT_WIDTH 1296
+#define S5K6A3_DEFAULT_HEIGHT 732
+
+#define S5K6A3_DRV_NAME "S5K6A3"
+#define S5K6A3_CLK_NAME "extclk"
+#define S5K6A3_DEFAULT_CLK_FREQ 24000000U
+
+enum {
+ S5K6A3_SUPP_VDDA,
+ S5K6A3_SUPP_VDDIO,
+ S5K6A3_SUPP_AFVDD,
+ S5K6A3_NUM_SUPPLIES,
+};
+
+/**
+ * struct s5k6a3 - fimc-is sensor data structure
+ * @dev: pointer to this I2C client device structure
+ * @subdev: the image sensor's v4l2 subdev
+ * @pad: subdev media source pad
+ * @supplies: image sensor's voltage regulator supplies
+ * @gpio_reset: GPIO connected to the sensor's reset pin
+ * @lock: mutex protecting the structure's members below
+ * @format: media bus format at the sensor's source pad
+ */
+struct s5k6a3 {
+ struct device *dev;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
+ int gpio_reset;
+ struct mutex lock;
+ struct v4l2_mbus_framefmt format;
+ struct clk *clock;
+ u32 clock_frequency;
+ int power_count;
+};
+
+static const char * const s5k6a3_supply_names[] = {
+ [S5K6A3_SUPP_VDDA] = "svdda",
+ [S5K6A3_SUPP_VDDIO] = "svddio",
+ [S5K6A3_SUPP_AFVDD] = "afvdd",
+};
+
+static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6a3, subdev);
+}
+
+static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ }
+};
+
+static const struct v4l2_mbus_framefmt *find_sensor_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
+ if (mf->code == s5k6a3_formats[i].code)
+ return &s5k6a3_formats[i];
+
+ return &s5k6a3_formats[0];
+}
+
+static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6a3_formats))
+ return -EINVAL;
+
+ code->code = s5k6a3_formats[code->index].code;
+ return 0;
+}
+
+static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = find_sensor_format(mf);
+ mf->code = fmt->code;
+ v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
+ S5K6A3_SENSOR_MAX_WIDTH, 0,
+ &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
+ S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
+}
+
+static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
+ struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
+ u32 pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+ return &sensor->format;
+}
+
+static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ s5k6a3_try_format(&fmt->format);
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+ if (mf) {
+ mutex_lock(&sensor->lock);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ *mf = fmt->format;
+ mutex_unlock(&sensor->lock);
+ }
+ return 0;
+}
+
+static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *mf;
+ mutex_unlock(&sensor->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
+ .enum_mbus_code = s5k6a3_enum_mbus_code,
+ .get_fmt = s5k6a3_get_fmt,
+ .set_fmt = s5k6a3_set_fmt,
+};
+
+static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ *format = s5k6a3_formats[0];
+ format->width = S5K6A3_DEFAULT_WIDTH;
+ format->height = S5K6A3_DEFAULT_HEIGHT;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
+ .open = s5k6a3_open,
+};
+
+static int __s5k6a3_power_on(struct s5k6a3 *sensor)
+{
+ int i = S5K6A3_SUPP_VDDA;
+ int ret;
+
+ ret = clk_set_rate(sensor->clock, sensor->clock_frequency);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_get(sensor->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_rpm_put;
+
+ ret = clk_prepare_enable(sensor->clock);
+ if (ret < 0)
+ goto error_reg_dis;
+
+ for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_reg_dis;
+ }
+
+ gpio_set_value(sensor->gpio_reset, 1);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 0);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 1);
+
+ /* Delay needed for the sensor initialization */
+ msleep(20);
+ return 0;
+
+error_reg_dis:
+ for (--i; i >= 0; --i)
+ regulator_disable(sensor->supplies[i].consumer);
+error_rpm_put:
+ pm_runtime_put(sensor->dev);
+ return ret;
+}
+
+static int __s5k6a3_power_off(struct s5k6a3 *sensor)
+{
+ int i;
+
+ gpio_set_value(sensor->gpio_reset, 0);
+
+ for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
+ regulator_disable(sensor->supplies[i].consumer);
+
+ clk_disable_unprepare(sensor->clock);
+ pm_runtime_put(sensor->dev);
+ return 0;
+}
+
+static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->power_count == !on) {
+ if (on)
+ ret = __s5k6a3_power_on(sensor);
+ else
+ ret = __s5k6a3_power_off(sensor);
+
+ if (ret == 0)
+ sensor->power_count += on ? 1 : -1;
+ }
+
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops s5k6a3_core_ops = {
+ .s_power = s5k6a3_s_power,
+};
+
+static struct v4l2_subdev_ops s5k6a3_subdev_ops = {
+ .core = &s5k6a3_core_ops,
+ .pad = &s5k6a3_pad_ops,
+};
+
+static int s5k6a3_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct s5k6a3 *sensor;
+ struct v4l2_subdev *sd;
+ int gpio, i, ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+ sensor->gpio_reset = -EINVAL;
+ sensor->clock = ERR_PTR(-EINVAL);
+ sensor->dev = dev;
+
+ sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
+ if (IS_ERR(sensor->clock))
+ return PTR_ERR(sensor->clock);
+
+ gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
+ if (!gpio_is_valid(gpio))
+ return gpio;
+
+ ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
+ S5K6A3_DRV_NAME);
+ if (ret < 0)
+ return ret;
+
+ sensor->gpio_reset = gpio;
+
+ if (of_property_read_u32(dev->of_node, "clock-frequency",
+ &sensor->clock_frequency)) {
+ sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ sensor->clock_frequency);
+ }
+
+ for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
+ sensor->supplies[i].supply = s5k6a3_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ sd = &sensor->subdev;
+ v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ sensor->format.code = s5k6a3_formats[0].code;
+ sensor->format.width = S5K6A3_DEFAULT_WIDTH;
+ sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
+
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(sd);
+
+ if (ret < 0) {
+ pm_runtime_disable(&client->dev);
+ media_entity_cleanup(&sd->entity);
+ }
+
+ return ret;
+}
+
+static int s5k6a3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ return 0;
+}
+
+static const struct i2c_device_id s5k6a3_ids[] = {
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id s5k6a3_of_match[] = {
+ { .compatible = "samsung,s5k6a3" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
+#endif
+
+static struct i2c_driver s5k6a3_driver = {
+ .driver = {
+ .of_match_table = of_match_ptr(s5k6a3_of_match),
+ .name = S5K6A3_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = s5k6a3_probe,
+ .remove = s5k6a3_remove,
+ .id_table = s5k6a3_ids,
+};
+
+module_i2c_driver(s5k6a3_driver);
+
+MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 05/10] V4L: s5c73m3: Add device tree support
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This patch adds the V4L2 asynchronous subdev registration and
device tree support. Common clock API is used to control the
sensor master clock from within the subdev.
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
Changes since v3:
- move clk_get from subdev s_power() to driver probe() callback and
make it devm_clk_get(),
- pass any errors from dev_clk_get() through, rather than overwriting
with EPROBE_DEFER. Patch [1] is required for things to work correctly
after this change.
[1] http://www.spinics.net/lists/arm-kernel/msg306072.html
---
drivers/media/i2c/s5c73m3/s5c73m3-core.c | 207 ++++++++++++++++++++++--------
drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 +
drivers/media/i2c/s5c73m3/s5c73m3.h | 4 +
3 files changed, 167 insertions(+), 50 deletions(-)
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index e7f555c..a445930 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/sizes.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
@@ -23,7 +23,9 @@
#include <linux/init.h>
#include <linux/media.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/videodev2.h>
@@ -33,6 +35,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/s5c73m3.h>
+#include <media/v4l2-of.h>
#include "s5c73m3.h"
@@ -46,6 +49,8 @@ static int update_fw;
module_param(update_fw, int, 0644);
#define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K
+#define S5C73M3_MIPI_DATA_LANES 4
+#define S5C73M3_CLK_NAME "cis_extclk"
static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {
"vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */
@@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {
ret = regulator_enable(state->supplies[i].consumer);
if (ret)
- goto err;
+ goto err_reg_dis;
}
+ ret = clk_set_rate(state->clock, state->mclk_frequency);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ ret = clk_prepare_enable(state->clock);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n",
+ clk_get_rate(state->clock));
+
s5c73m3_gpio_deassert(state, STBY);
usleep_range(100, 200);
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
usleep_range(50, 100);
return 0;
-err:
+
+err_reg_dis:
for (--i; i >= 0; i--)
regulator_disable(state->supplies[i].consumer);
return ret;
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (s5c73m3_gpio_assert(state, STBY))
usleep_range(100, 200);
+
+ clk_disable_unprepare(state->clock);
+
state->streaming = 0;
state->isp_ready = 0;
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (ret)
goto err;
}
+
return 0;
err:
for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
@@ -1396,6 +1417,8 @@ err:
v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
state->supplies[i].supply, r);
}
+
+ clk_prepare_enable(state->clock);
return ret;
}
@@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)
S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- mutex_lock(&state->lock);
- ret = __s5c73m3_power_on(state);
- if (ret == 0)
- s5c73m3_get_fw_version(state);
-
- __s5c73m3_power_off(state);
- mutex_unlock(&state->lock);
-
- v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n",
- __func__, ret ? "failed" : "succeeded", ret);
-
return ret;
}
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_configure_gpios(struct s5c73m3 *state,
- const struct s5c73m3_platform_data *pdata)
+static int s5c73m3_configure_gpios(struct s5c73m3 *state)
+{
+ static const char * const gpio_names[] = {
+ "S5C73M3_STBY", "S5C73M3_RST"
+ };
+ struct i2c_client *c = state->i2c_client;
+ struct s5c73m3_gpio *g = state->gpio;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ unsigned int flags = GPIOF_DIR_OUT;
+ if (g[i].level)
+ flags |= GPIOF_INIT_HIGH;
+ ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags,
+ gpio_names[i]);
+ if (ret) {
+ v4l2_err(c, "failed to request gpio %s\n",
+ gpio_names[i]);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s5c73m3_parse_gpios(struct s5c73m3 *state)
+{
+ static const char * const prop_names[] = {
+ "standby-gpios", "xshutdown-gpios",
+ };
+ struct device *dev = &state->i2c_client->dev;
+ struct device_node *node = dev->of_node;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ enum of_gpio_flags of_flags;
+
+ ret = of_get_named_gpio_flags(node, prop_names[i],
+ 0, &of_flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse %s DT property\n",
+ prop_names[i]);
+ return -EINVAL;
+ }
+ state->gpio[i].gpio = ret;
+ state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW);
+ }
+ return 0;
+}
+
+static int s5c73m3_get_platform_data(struct s5c73m3 *state)
{
struct device *dev = &state->i2c_client->dev;
- const struct s5c73m3_gpio *gpio;
- unsigned long flags;
+ const struct s5c73m3_platform_data *pdata = dev->platform_data;
+ struct device_node *node = dev->of_node;
+ struct device_node *node_ep;
+ struct v4l2_of_endpoint ep;
int ret;
- state->gpio[STBY].gpio = -EINVAL;
- state->gpio[RST].gpio = -EINVAL;
+ if (!node) {
+ if (!pdata) {
+ dev_err(dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
- gpio = &pdata->gpio_stby;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_STBY");
- if (ret < 0)
- return ret;
+ state->mclk_frequency = pdata->mclk_frequency;
+ state->gpio[STBY] = pdata->gpio_stby;
+ state->gpio[RST] = pdata->gpio_reset;
+ return 0;
+ }
+
+ state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
+ if (IS_ERR(state->clock))
+ return PTR_ERR(state->clock);
- state->gpio[STBY] = *gpio;
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->mclk_frequency)) {
+ state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ state->mclk_frequency);
}
- gpio = &pdata->gpio_reset;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_RST");
- if (ret < 0)
- return ret;
+ ret = s5c73m3_parse_gpios(state);
+ if (ret < 0)
+ return -EINVAL;
- state->gpio[RST] = *gpio;
+ node_ep = v4l2_of_get_next_endpoint(node, NULL);
+ if (!node_ep) {
+ dev_warn(dev, "no endpoint defined for node: %s\n",
+ node->full_name);
+ return 0;
}
+ v4l2_of_parse_endpoint(node_ep, &ep);
+ of_node_put(node_ep);
+
+ if (ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "unsupported bus type\n");
+ return -EINVAL;
+ }
+ /*
+ * Number of MIPI CSI-2 data lanes is currently not configurable,
+ * always a default value of 4 lanes is used.
+ */
+ if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES)
+ dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n");
+
return 0;
}
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- const struct s5c73m3_platform_data *pdata = client->dev.platform_data;
struct v4l2_subdev *sd;
struct v4l2_subdev *oif_sd;
struct s5c73m3 *state;
int ret, i;
- if (pdata == NULL) {
- dev_err(&client->dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
+ state->i2c_client = client;
+ ret = s5c73m3_get_platform_data(state);
+ if (ret < 0)
+ return ret;
+
mutex_init(&state->lock);
sd = &state->sensor_sd;
oif_sd = &state->oif_sd;
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- state->mclk_frequency = pdata->mclk_frequency;
- state->bus_type = pdata->bus_type;
- state->i2c_client = client;
-
- ret = s5c73m3_configure_gpios(state, pdata);
+ ret = s5c73m3_configure_gpios(state);
if (ret)
goto out_err;
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
goto out_err;
+ oif_sd->dev = dev;
+
+ ret = __s5c73m3_power_on(state);
+ if (ret < 0)
+ goto out_err1;
+
+ ret = s5c73m3_get_fw_version(state);
+ __s5c73m3_power_off(state);
+
+ if (ret < 0) {
+ dev_err(dev, "Device detection failed: %d\n", ret);
+ goto out_err1;
+ }
+
+ ret = v4l2_async_register_subdev(oif_sd);
+ if (ret < 0)
+ goto out_err1;
+
v4l2_info(sd, "%s: completed successfully\n", __func__);
return 0;
+out_err1:
+ s5c73m3_unregister_spi_driver(state);
out_err:
media_entity_cleanup(&sd->entity);
return ret;
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)
struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);
struct v4l2_subdev *sensor_sd = &state->sensor_sd;
- v4l2_device_unregister_subdev(oif_sd);
+ v4l2_async_unregister_subdev(oif_sd);
v4l2_ctrl_handler_free(oif_sd->ctrl_handler);
media_entity_cleanup(&oif_sd->entity);
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = {
};
MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
+#ifdef CONFIG_OF
+static const struct of_device_id s5c73m3_of_match[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s5c73m3_of_match);
+#endif
+
static struct i2c_driver s5c73m3_i2c_driver = {
.driver = {
+ .of_match_table = of_match_ptr(s5c73m3_of_match),
.name = DRIVER_NAME,
},
.probe = s5c73m3_probe,
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index 8079e26..f60b265 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -27,6 +27,11 @@
#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
+static const struct of_device_id s5c73m3_spi_ids[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+
enum spi_direction {
SPI_DIR_RX,
SPI_DIR_TX
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)
spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
spidrv->driver.bus = &spi_bus_type;
spidrv->driver.owner = THIS_MODULE;
+ spidrv->driver.of_match_table = s5c73m3_spi_ids;
return spi_register_driver(spidrv);
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 9dfa516..9656b67 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -17,6 +17,7 @@
#ifndef S5C73M3_H_
#define S5C73M3_H_
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-common.h>
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {
#define S5C73M3_MAX_SUPPLIES 6
+#define S5C73M3_DEFAULT_MCLK_FREQ 24000000U
struct s5c73m3_ctrls {
struct v4l2_ctrl_handler handler;
@@ -391,6 +393,8 @@ struct s5c73m3 {
struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
struct s5c73m3_gpio gpio[GPIO_NUM];
+ struct clk *clock;
+
/* External master clock frequency */
u32 mclk_frequency;
/* Video bus type - MIPI-CSI2/parallel */
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 06/10] exynos4-is: Use external s5k6a3 sensor driver
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This patch removes the common fimc-is-sensor driver for image sensors
that are normally controlled by the FIMC-IS firmware. The FIMC-IS
driver now contains only a table of properties specific to each sensor.
The sensor properties required for the ISP's firmware are parsed from
device tree and retrieved from the internal table, which is selected
based on the compatible property of an image sensor.
To use the Exynos4x12 internal ISP the S5K6A3 sensor driver (drivers/
media/i2c/s5k6a3.c) is now required.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/media/platform/exynos4-is/fimc-is-regs.c | 2 +-
drivers/media/platform/exynos4-is/fimc-is-sensor.c | 285 +-------------------
drivers/media/platform/exynos4-is/fimc-is-sensor.h | 49 +---
drivers/media/platform/exynos4-is/fimc-is.c | 97 +++----
drivers/media/platform/exynos4-is/fimc-is.h | 4 +-
5 files changed, 57 insertions(+), 380 deletions(-)
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index 2628733..5c7bd2a 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -112,7 +112,7 @@ void fimc_is_hw_set_sensor_num(struct fimc_is *is)
mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
- mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3));
+ mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));
}
void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
index 6647421..10e82e2 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
@@ -2,276 +2,21 @@
* Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
- *
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* 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.
*/
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_gpio.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <media/v4l2-subdev.h>
-#include "fimc-is.h"
#include "fimc-is-sensor.h"
-#define DRIVER_NAME "FIMC-IS-SENSOR"
-
-static const char * const sensor_supply_names[] = {
- "svdda",
- "svddio",
-};
-
-static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = {
- {
- .code = V4L2_MBUS_FMT_SGRBG10_1X10,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .field = V4L2_FIELD_NONE,
- }
-};
-
-static const struct v4l2_mbus_framefmt *find_sensor_format(
- struct v4l2_mbus_framefmt *mf)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++)
- if (mf->code == fimc_is_sensor_formats[i].code)
- return &fimc_is_sensor_formats[i];
-
- return &fimc_is_sensor_formats[0];
-}
-
-static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats))
- return -EINVAL;
-
- code->code = fimc_is_sensor_formats[code->index].code;
- return 0;
-}
-
-static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor,
- struct v4l2_mbus_framefmt *mf)
-{
- const struct sensor_drv_data *dd = sensor->drvdata;
- const struct v4l2_mbus_framefmt *fmt;
-
- fmt = find_sensor_format(mf);
- mf->code = fmt->code;
- v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0,
- &mf->height, 12 + 8, dd->height, 0, 0);
-}
-
-static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format(
- struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh,
- u32 pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
-
- return &sensor->format;
-}
-
-static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- struct v4l2_mbus_framefmt *mf;
-
- fimc_is_sensor_try_format(sensor, &fmt->format);
-
- mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
- if (mf) {
- mutex_lock(&sensor->lock);
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- *mf = fmt->format;
- mutex_unlock(&sensor->lock);
- }
- return 0;
-}
-
-static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- struct v4l2_mbus_framefmt *mf;
-
- mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
-
- mutex_lock(&sensor->lock);
- fmt->format = *mf;
- mutex_unlock(&sensor->lock);
- return 0;
-}
-
-static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = {
- .enum_mbus_code = fimc_is_sensor_enum_mbus_code,
- .get_fmt = fimc_is_sensor_get_fmt,
- .set_fmt = fimc_is_sensor_set_fmt,
-};
-
-static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-
- *format = fimc_is_sensor_formats[0];
- format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
- format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
- return 0;
-}
-
-static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = {
- .open = fimc_is_sensor_open,
-};
-
-static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on)
-{
- struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
- int gpio = sensor->gpio_reset;
- int ret;
-
- if (on) {
- ret = pm_runtime_get(sensor->dev);
- if (ret < 0)
- return ret;
-
- ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (ret < 0) {
- pm_runtime_put(sensor->dev);
- return ret;
- }
- if (gpio_is_valid(gpio)) {
- gpio_set_value(gpio, 1);
- usleep_range(600, 800);
- gpio_set_value(gpio, 0);
- usleep_range(10000, 11000);
- gpio_set_value(gpio, 1);
- }
-
- /* A delay needed for the sensor initialization. */
- msleep(20);
- } else {
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, 0);
-
- ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (!ret)
- pm_runtime_put(sensor->dev);
- }
-
- pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret);
-
- return ret;
-}
-
-static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = {
- .s_power = fimc_is_sensor_s_power,
-};
-
-static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = {
- .core = &fimc_is_sensor_core_ops,
- .pad = &fimc_is_sensor_pad_ops,
-};
-
-static const struct of_device_id fimc_is_sensor_of_match[];
-
-static int fimc_is_sensor_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device *dev = &client->dev;
- struct fimc_is_sensor *sensor;
- const struct of_device_id *of_id;
- struct v4l2_subdev *sd;
- int gpio, i, ret;
-
- sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
- if (!sensor)
- return -ENOMEM;
-
- mutex_init(&sensor->lock);
- sensor->gpio_reset = -EINVAL;
-
- gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
- DRIVER_NAME);
- if (ret < 0)
- return ret;
- }
- sensor->gpio_reset = gpio;
-
- for (i = 0; i < SENSOR_NUM_SUPPLIES; i++)
- sensor->supplies[i].supply = sensor_supply_names[i];
-
- ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES,
- sensor->supplies);
- if (ret < 0)
- return ret;
-
- of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node);
- if (!of_id)
- return -ENODEV;
-
- sensor->drvdata = of_id->data;
- sensor->dev = dev;
-
- sd = &sensor->subdev;
- v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops);
- snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name);
- sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- sensor->format.code = fimc_is_sensor_formats[0].code;
- sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
- sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
-
- sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
- if (ret < 0)
- return ret;
-
- pm_runtime_no_callbacks(dev);
- pm_runtime_enable(dev);
-
- return ret;
-}
-
-static int fimc_is_sensor_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- media_entity_cleanup(&sd->entity);
- return 0;
-}
-
-static const struct i2c_device_id fimc_is_sensor_ids[] = {
- { }
-};
-
static const struct sensor_drv_data s5k6a3_drvdata = {
.id = FIMC_IS_SENSOR_ID_S5K6A3,
- .subdev_name = "S5K6A3",
- .width = S5K6A3_SENSOR_WIDTH,
- .height = S5K6A3_SENSOR_HEIGHT,
+ .open_timeout = S5K6A3_OPEN_TIMEOUT,
};
-static const struct of_device_id fimc_is_sensor_of_match[] = {
+static const struct of_device_id fimc_is_sensor_of_ids[] = {
{
.compatible = "samsung,s5k6a3",
.data = &s5k6a3_drvdata,
@@ -279,27 +24,11 @@ static const struct of_device_id fimc_is_sensor_of_match[] = {
{ }
};
-static struct i2c_driver fimc_is_sensor_driver = {
- .driver = {
- .of_match_table = fimc_is_sensor_of_match,
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
- .probe = fimc_is_sensor_probe,
- .remove = fimc_is_sensor_remove,
- .id_table = fimc_is_sensor_ids,
-};
-
-int fimc_is_register_sensor_driver(void)
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node)
{
- return i2c_add_driver(&fimc_is_sensor_driver);
-}
+ const struct of_device_id *of_id;
-void fimc_is_unregister_sensor_driver(void)
-{
- i2c_del_driver(&fimc_is_sensor_driver);
+ of_id = of_match_node(fimc_is_sensor_of_ids, node);
+ return of_id ? of_id->data : NULL;
}
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
index 6036d49..173ccff 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
@@ -13,24 +13,13 @@
#ifndef FIMC_IS_SENSOR_H_
#define FIMC_IS_SENSOR_H_
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-
-#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */
-
-#define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296
-#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732
+#include <linux/of.h>
+#include <linux/types.h>
+#define S5K6A3_OPEN_TIMEOUT 2000 /* ms */
#define S5K6A3_SENSOR_WIDTH 1392
#define S5K6A3_SENSOR_HEIGHT 1392
-#define SENSOR_NUM_SUPPLIES 2
-
enum fimc_is_sensor_id {
FIMC_IS_SENSOR_ID_S5K3H2 = 1,
FIMC_IS_SENSOR_ID_S5K6A3,
@@ -45,45 +34,23 @@ enum fimc_is_sensor_id {
struct sensor_drv_data {
enum fimc_is_sensor_id id;
- const char * const subdev_name;
- unsigned int width;
- unsigned int height;
+ /* sensor open timeout in ms */
+ unsigned short open_timeout;
};
/**
* struct fimc_is_sensor - fimc-is sensor data structure
- * @dev: pointer to this I2C client device structure
- * @subdev: the image sensor's v4l2 subdev
- * @pad: subdev media source pad
- * @supplies: image sensor's voltage regulator supplies
- * @gpio_reset: GPIO connected to the sensor's reset pin
* @drvdata: a pointer to the sensor's parameters data structure
* @i2c_bus: ISP I2C bus index (0...1)
* @test_pattern: true to enable video test pattern
- * @lock: mutex protecting the structure's members below
- * @format: media bus format at the sensor's source pad
*/
struct fimc_is_sensor {
- struct device *dev;
- struct v4l2_subdev subdev;
- struct media_pad pad;
- struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES];
- int gpio_reset;
const struct sensor_drv_data *drvdata;
unsigned int i2c_bus;
- bool test_pattern;
-
- struct mutex lock;
- struct v4l2_mbus_framefmt format;
+ u8 test_pattern;
};
-static inline
-struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct fimc_is_sensor, subdev);
-}
-
-int fimc_is_register_sensor_driver(void);
-void fimc_is_unregister_sensor_driver(void);
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node);
#endif /* FIMC_IS_SENSOR_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 13a4228..a8f5845 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -161,78 +161,66 @@ static void fimc_is_disable_clocks(struct fimc_is *is)
}
}
-static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor,
- struct device_node *np)
+static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
+ struct device_node *node)
{
+ struct fimc_is_sensor *sensor = &is->sensor[index];
u32 tmp = 0;
int ret;
- np = v4l2_of_get_next_endpoint(np, NULL);
- if (!np)
+ sensor->drvdata = fimc_is_sensor_get_drvdata(node);
+ if (!sensor->drvdata) {
+ dev_err(&is->pdev->dev, "no driver data found for: %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ node = v4l2_of_get_next_endpoint(node, NULL);
+ if (!node)
return -ENXIO;
- np = v4l2_of_get_remote_port(np);
- if (!np)
+
+ node = v4l2_of_get_remote_port(node);
+ if (!node)
return -ENXIO;
/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
- ret = of_property_read_u32(np, "reg", &tmp);
- sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ ret = of_property_read_u32(node, "reg", &tmp);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+ node->full_name);
+ return ret;
+ }
- return ret;
+ sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ return 0;
}
static int fimc_is_register_subdevs(struct fimc_is *is)
{
- struct device_node *adapter, *child;
- int ret;
+ struct device_node *i2c_bus, *child;
+ int ret, index = 0;
ret = fimc_isp_subdev_create(&is->isp);
if (ret < 0)
return ret;
- for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) {
- if (!of_find_device_by_node(adapter)) {
- of_node_put(adapter);
- return -EPROBE_DEFER;
- }
+ for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_available_child_of_node(i2c_bus, child) {
+ ret = fimc_is_parse_sensor_config(is, index, child);
- for_each_available_child_of_node(adapter, child) {
- struct i2c_client *client;
- struct v4l2_subdev *sd;
-
- client = of_find_i2c_device_by_node(child);
- if (!client)
- goto e_retry;
-
- sd = i2c_get_clientdata(client);
- if (!sd)
- goto e_retry;
-
- /* FIXME: Add support for multiple sensors. */
- if (WARN_ON(is->sensor))
- continue;
-
- is->sensor = sd_to_fimc_is_sensor(sd);
-
- if (fimc_is_parse_sensor_config(is->sensor, child)) {
- dev_warn(&is->pdev->dev, "DT parse error: %s\n",
- child->full_name);
+ if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
+ of_node_put(child);
+ return ret;
}
- pr_debug("%s(): registered subdev: %p\n",
- __func__, sd->name);
+ index++;
}
}
return 0;
-
-e_retry:
- of_node_put(child);
- return -EPROBE_DEFER;
}
static int fimc_is_unregister_subdevs(struct fimc_is *is)
{
fimc_isp_subdev_destroy(&is->isp);
- is->sensor = NULL;
return 0;
}
@@ -647,7 +635,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is,
fimc_is_hw_set_intgr0_gd0(is);
return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
- FIMC_IS_SENSOR_OPEN_TIMEOUT);
+ sensor->drvdata->open_timeout);
}
@@ -661,8 +649,8 @@ int fimc_is_hw_initialize(struct fimc_is *is)
u32 prev_id;
int i, ret;
- /* Sensor initialization. */
- ret = fimc_is_hw_open_sensor(is, is->sensor);
+ /* Sensor initialization. Only one sensor is currently supported. */
+ ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
if (ret < 0)
return ret;
@@ -977,27 +965,20 @@ static int fimc_is_module_init(void)
{
int ret;
- ret = fimc_is_register_sensor_driver();
- if (ret < 0)
- return ret;
-
ret = fimc_is_register_i2c_driver();
if (ret < 0)
- goto err_sens;
+ return ret;
ret = platform_driver_register(&fimc_is_driver);
- if (!ret)
- return ret;
- fimc_is_unregister_i2c_driver();
-err_sens:
- fimc_is_unregister_sensor_driver();
+ if (ret < 0)
+ fimc_is_unregister_i2c_driver();
+
return ret;
}
static void fimc_is_module_exit(void)
{
- fimc_is_unregister_sensor_driver();
fimc_is_unregister_i2c_driver();
platform_driver_unregister(&fimc_is_driver);
}
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
index 61bb012..01f802f 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -39,7 +39,7 @@
#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */
#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */
-#define FIMC_IS_SENSOR_NUM 2
+#define FIMC_IS_SENSORS_NUM 2
/* Memory definitions */
#define FIMC_IS_CPU_MEM_SIZE (0xa00000)
@@ -253,7 +253,7 @@ struct fimc_is {
struct firmware *f_w;
struct fimc_isp isp;
- struct fimc_is_sensor *sensor;
+ struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM];
struct fimc_is_setfile setfile;
struct vb2_alloc_ctx *alloc_ctx;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 07/10] exynos4-is: Add clock provider for the SCLK_CAM clock outputs
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This patch adds clock provider so the the SCLK_CAM0/1 output clocks
can be accessed by image sensor devices through the clk API.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
Changes since v3:
- use clock-output-names DT property instead of hard coding names
of registered clocks in the driver; first two entries of the
clock-names property value are used to specify parent clocks of
cam_{a,b}_clkout clocks, rather than hard coding it to sclk_cam{0,1}
in the driver.
- addressed issues pointed out in review by Mauro.
Changes since v2:
- use 'camera' DT node drirectly as clock provider node, rather than
creating additional clock-controller child node.
- DT binding documentation moved to a separate patch.
---
drivers/media/platform/exynos4-is/media-dev.c | 110 +++++++++++++++++++++++++
drivers/media/platform/exynos4-is/media-dev.h | 19 ++++-
2 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index c1bce17..dfc3131 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -11,6 +11,8 @@
*/
#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
@@ -1437,6 +1439,103 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)
return 0;
}
+#ifdef CONFIG_OF
+static int cam_clk_prepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+ int ret;
+
+ if (camclk->fmd->pmf == NULL)
+ return -ENODEV;
+
+ ret = pm_runtime_get_sync(camclk->fmd->pmf);
+ return ret < 0 ? ret : 0;
+}
+
+static void cam_clk_unprepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+
+ if (camclk->fmd->pmf == NULL)
+ return;
+
+ pm_runtime_put_sync(camclk->fmd->pmf);
+}
+
+static const struct clk_ops cam_clk_ops = {
+ .prepare = cam_clk_prepare,
+ .unprepare = cam_clk_unprepare,
+};
+
+static void fimc_md_unregister_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ unsigned int i;
+
+ if (cp->of_node)
+ of_clk_del_provider(cp->of_node);
+
+ for (i = 0; i < cp->num_clocks; i++)
+ clk_unregister(cp->clks[i]);
+}
+
+static int fimc_md_register_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ struct device *dev = &fmd->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(cp->clks); i++) {
+ struct cam_clk *camclk = &cp->camclk[i];
+ struct clk_init_data init;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "clock-output-names", i, &init.name);
+ if (ret < 0)
+ break;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "clock-names", i, init.parent_names);
+ if (ret < 0)
+ break;
+
+ init.num_parents = 1;
+ init.ops = &cam_clk_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ camclk->hw.init = &init;
+ camclk->fmd = fmd;
+
+ cp->clks[i] = clk_register(NULL, &camclk->hw);
+ if (IS_ERR(cp->clks[i])) {
+ dev_err(dev, "failed to register clock: %s (%ld)\n",
+ init.name, PTR_ERR(cp->clks[i]));
+ ret = PTR_ERR(cp->clks[i]);
+ goto err;
+ }
+ cp->num_clocks++;
+ }
+
+ if (cp->num_clocks == 0) {
+ dev_warn(dev, "clk provider not registered\n");
+ return 0;
+ }
+
+ cp->clk_data.clks = cp->clks;
+ cp->clk_data.clk_num = cp->num_clocks;
+ cp->of_node = dev->of_node;
+ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+ &cp->clk_data);
+ if (ret == 0)
+ return 0;
+err:
+ fimc_md_unregister_clk_provider(fmd);
+ return ret;
+}
+#else
+#define fimc_md_register_clk_provider(fmd) (0)
+#define fimc_md_unregister_clk_provider(fmd) (0)
+#endif
+
static int fimc_md_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1464,16 +1563,24 @@ static int fimc_md_probe(struct platform_device *pdev)
fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
+ ret = fimc_md_register_clk_provider(fmd);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "clock provider registration failed\n");
+ return ret;
+ }
+
ret = v4l2_device_register(dev, &fmd->v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
return ret;
}
+
ret = media_device_register(&fmd->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
goto err_md;
}
+
ret = fimc_md_get_clocks(fmd);
if (ret)
goto err_clk;
@@ -1507,6 +1614,7 @@ static int fimc_md_probe(struct platform_device *pdev)
ret = fimc_md_create_links(fmd);
if (ret)
goto err_unlock;
+
ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
if (ret)
goto err_unlock;
@@ -1527,6 +1635,7 @@ err_clk:
media_device_unregister(&fmd->media_dev);
err_md:
v4l2_device_unregister(&fmd->v4l2_dev);
+ fimc_md_unregister_clk_provider(fmd);
return ret;
}
@@ -1537,6 +1646,7 @@ static int fimc_md_remove(struct platform_device *pdev)
if (!fmd)
return 0;
+ fimc_md_unregister_clk_provider(fmd);
v4l2_device_unregister(&fmd->v4l2_dev);
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 62599fd..a88cee5 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -10,6 +10,7 @@
#define FIMC_MDEVICE_H_
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -89,6 +90,12 @@ struct fimc_sensor_info {
struct fimc_dev *host;
};
+struct cam_clk {
+ struct clk_hw hw;
+ struct fimc_md *fmd;
+};
+#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw)
+
/**
* struct fimc_md - fimc media device information
* @csis: MIPI CSIS subdevs data
@@ -105,6 +112,7 @@ struct fimc_sensor_info {
* @pinctrl: camera port pinctrl handle
* @state_default: pinctrl default state handle
* @state_idle: pinctrl idle state handle
+ * @cam_clk_provider: CAMCLK clock provider structure
* @user_subdev_api: true if subdevs are not configured by the host driver
* @slock: spinlock protecting @sensor array
*/
@@ -122,13 +130,22 @@ struct fimc_md {
struct media_device media_dev;
struct v4l2_device v4l2_dev;
struct platform_device *pdev;
+
struct fimc_pinctrl {
struct pinctrl *pinctrl;
struct pinctrl_state *state_default;
struct pinctrl_state *state_idle;
} pinctl;
- bool user_subdev_api;
+ struct cam_clk_provider {
+ struct clk *clks[FIMC_MAX_CAMCLKS];
+ struct clk_onecell_data clk_data;
+ struct device_node *of_node;
+ struct cam_clk camclk[FIMC_MAX_CAMCLKS];
+ int num_clocks;
+ } clk_provider;
+
+ bool user_subdev_api;
spinlock_t slock;
struct list_head pipelines;
};
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 08/10] exynos4-is: Add support for asynchronous subdevices registration
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
Add support for registering external sensor subdevs using v4l2-async
API. The async API is used only for sensor subdevs and only for DT
platforms.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/media/platform/exynos4-is/media-dev.c | 233 ++++++++++++++-----------
drivers/media/platform/exynos4-is/media-dev.h | 13 +-
2 files changed, 144 insertions(+), 102 deletions(-)
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index dfc3131..127ced9 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -26,6 +26,7 @@
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include <media/media-device.h>
@@ -220,6 +221,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
if (ret < 0)
return ret;
}
+
ret = fimc_md_set_camclk(sd, true);
if (ret < 0)
goto err_wbclk;
@@ -380,77 +382,18 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adapter;
- if (!client)
+ if (!client || client->dev.of_node)
return;
v4l2_device_unregister_subdev(sd);
- if (!client->dev.of_node) {
- adapter = client->adapter;
- i2c_unregister_device(client);
- if (adapter)
- i2c_put_adapter(adapter);
- }
+ adapter = client->adapter;
+ i2c_unregister_device(client);
+ if (adapter)
+ i2c_put_adapter(adapter);
}
#ifdef CONFIG_OF
-/* Register I2C client subdev associated with @node. */
-static int fimc_md_of_add_sensor(struct fimc_md *fmd,
- struct device_node *node, int index)
-{
- struct fimc_sensor_info *si;
- struct i2c_client *client;
- struct v4l2_subdev *sd;
- int ret;
-
- if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
- return -EINVAL;
- si = &fmd->sensor[index];
-
- client = of_find_i2c_device_by_node(node);
- if (!client)
- return -EPROBE_DEFER;
-
- device_lock(&client->dev);
-
- if (!client->dev.driver ||
- !try_module_get(client->dev.driver->owner)) {
- ret = -EPROBE_DEFER;
- v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n",
- node->full_name);
- goto dev_put;
- }
-
- /* Enable sensor's master clock */
- ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
- if (ret < 0)
- goto mod_put;
- sd = i2c_get_clientdata(client);
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- __fimc_md_set_camclk(fmd, &si->pdata, false);
- if (ret < 0)
- goto mod_put;
-
- v4l2_set_subdev_hostdata(sd, &si->pdata);
- if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
- sd->grp_id = GRP_ID_FIMC_IS_SENSOR;
- else
- sd->grp_id = GRP_ID_SENSOR;
-
- si->subdev = sd;
- v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
- sd->name, fmd->num_sensors);
- fmd->num_sensors++;
-
-mod_put:
- module_put(client->dev.driver->owner);
-dev_put:
- device_unlock(&client->dev);
- put_device(&client->dev);
- return ret;
-}
-
/* Parse port node and register as a sub-device any sensor specified there. */
static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *port,
@@ -459,7 +402,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *rem, *ep, *np;
struct fimc_source_info *pd;
struct v4l2_of_endpoint endpoint;
- int ret;
u32 val;
pd = &fmd->sensor[index].pdata;
@@ -487,6 +429,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
if (!of_property_read_u32(rem, "clock-frequency", &val))
pd->clk_frequency = val;
+ else
+ pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ;
if (pd->clk_frequency == 0) {
v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
@@ -526,10 +470,17 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
else
pd->fimc_bus_type = pd->sensor_bus_type;
- ret = fimc_md_of_add_sensor(fmd, rem, index);
- of_node_put(rem);
+ if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
+ return -EINVAL;
- return ret;
+ fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
+ fmd->sensor[index].asd.match.of.node = rem;
+ fmd->async_subdevs[index] = &fmd->sensor[index].asd;
+
+ fmd->num_sensors++;
+
+ of_node_put(rem);
+ return 0;
}
/* Register all SoC external sub-devices */
@@ -885,11 +836,13 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
v4l2_device_unregister_subdev(fmd->csis[i].sd);
fmd->csis[i].sd = NULL;
}
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
- fimc_md_unregister_sensor(fmd->sensor[i].subdev);
- fmd->sensor[i].subdev = NULL;
+ if (fmd->pdev->dev.of_node == NULL) {
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
}
if (fmd->fimc_is)
@@ -1224,6 +1177,14 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
struct fimc_camclk_info *camclk;
int ret = 0;
+ /*
+ * When device tree is used the sensor drivers are supposed to
+ * control the clock themselves. This whole function will be
+ * removed once S5PV210 platform is converted to the device tree.
+ */
+ if (fmd->pdev->dev.of_node)
+ return 0;
+
if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
return -EINVAL;
@@ -1536,6 +1497,56 @@ err:
#define fimc_md_unregister_clk_provider(fmd) (0)
#endif
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ struct fimc_sensor_info *si = NULL;
+ int i;
+
+ /* Find platform data for this sensor subdev */
+ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
+ if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+ si = &fmd->sensor[i];
+
+ if (si == NULL)
+ return -EINVAL;
+
+ v4l2_set_subdev_hostdata(subdev, &si->pdata);
+
+ if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
+ subdev->grp_id = GRP_ID_FIMC_IS_SENSOR;
+ else
+ subdev->grp_id = GRP_ID_SENSOR;
+
+ si->subdev = subdev;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
+ subdev->name, fmd->num_sensors);
+
+ fmd->num_sensors++;
+
+ return 0;
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ int ret;
+
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
+ ret = fimc_md_create_links(fmd);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+unlock:
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return ret;
+}
+
static int fimc_md_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1563,12 +1574,6 @@ static int fimc_md_probe(struct platform_device *pdev)
fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
- ret = fimc_md_register_clk_provider(fmd);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "clock provider registration failed\n");
- return ret;
- }
-
ret = v4l2_device_register(dev, &fmd->v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
@@ -1578,64 +1583,87 @@ static int fimc_md_probe(struct platform_device *pdev)
ret = media_device_register(&fmd->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
- goto err_md;
+ goto err_v4l2_dev;
}
ret = fimc_md_get_clocks(fmd);
if (ret)
- goto err_clk;
+ goto err_md;
fmd->user_subdev_api = (dev->of_node != NULL);
- /* Protect the media graph while we're registering entities */
- mutex_lock(&fmd->media_dev.graph_mutex);
-
ret = fimc_md_get_pinctrl(fmd);
if (ret < 0) {
if (ret != EPROBE_DEFER)
dev_err(dev, "Failed to get pinctrl: %d\n", ret);
- goto err_unlock;
+ goto err_clk;
}
+ platform_set_drvdata(pdev, fmd);
+
+ /* Protect the media graph while we're registering entities */
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
if (dev->of_node)
ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
else
ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
fimc_md_pdev_match);
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_clk;
+ }
if (dev->platform_data || dev->of_node) {
ret = fimc_md_register_sensor_entities(fmd);
- if (ret)
- goto err_unlock;
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_m_ent;
+ }
}
- ret = fimc_md_create_links(fmd);
- if (ret)
- goto err_unlock;
+ mutex_unlock(&fmd->media_dev.graph_mutex);
- ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+ ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
if (ret)
- goto err_unlock;
+ goto err_m_ent;
+ /*
+ * FIMC platform devices need to be registered before the sclk_cam
+ * clocks provider, as one of these devices needs to be activated
+ * to enable the clock.
+ */
+ ret = fimc_md_register_clk_provider(fmd);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "clock provider registration failed\n");
+ goto err_attr;
+ }
- ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ fmd->subdev_notifier.subdevs = fmd->async_subdevs;
+ fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+ fmd->subdev_notifier.bound = subdev_notifier_bound;
+ fmd->subdev_notifier.complete = subdev_notifier_complete;
+ fmd->num_sensors = 0;
+
+ ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
+ &fmd->subdev_notifier);
if (ret)
- goto err_unlock;
+ goto err_clk_p;
- platform_set_drvdata(pdev, fmd);
- mutex_unlock(&fmd->media_dev.graph_mutex);
return 0;
-err_unlock:
- mutex_unlock(&fmd->media_dev.graph_mutex);
+err_clk_p:
+ fimc_md_unregister_clk_provider(fmd);
+err_attr:
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
err_clk:
fimc_md_put_clocks(fmd);
+err_m_ent:
fimc_md_unregister_entities(fmd);
media_device_unregister(&fmd->media_dev);
err_md:
+ media_device_unregister(&fmd->media_dev);
+err_v4l2_dev:
v4l2_device_unregister(&fmd->v4l2_dev);
- fimc_md_unregister_clk_provider(fmd);
return ret;
}
@@ -1647,12 +1675,15 @@ static int fimc_md_remove(struct platform_device *pdev)
return 0;
fimc_md_unregister_clk_provider(fmd);
+ v4l2_async_notifier_unregister(&fmd->subdev_notifier);
+
v4l2_device_unregister(&fmd->v4l2_dev);
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
fimc_md_pipelines_free(fmd);
media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
+
return 0;
}
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index a88cee5..ee1e251 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -32,8 +32,9 @@
#define PINCTRL_STATE_IDLE "idle"
-#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_SENSORS 4
#define FIMC_MAX_CAMCLKS 2
+#define DEFAULT_SENSOR_CLK_FREQ 24000000U
/* LCD/ISP Writeback clocks (PIXELASYNCMx) */
enum {
@@ -79,6 +80,7 @@ struct fimc_camclk_info {
/**
* struct fimc_sensor_info - image data source subdev information
* @pdata: sensor's atrributes passed as media device's platform data
+ * @asd: asynchronous subdev registration data structure
* @subdev: image sensor v4l2 subdev
* @host: fimc device the sensor is currently linked to
*
@@ -86,6 +88,7 @@ struct fimc_camclk_info {
*/
struct fimc_sensor_info {
struct fimc_source_info pdata;
+ struct v4l2_async_subdev asd;
struct v4l2_subdev *subdev;
struct fimc_dev *host;
};
@@ -145,6 +148,9 @@ struct fimc_md {
int num_clocks;
} clk_provider;
+ struct v4l2_async_notifier subdev_notifier;
+ struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+
bool user_subdev_api;
spinlock_t slock;
struct list_head pipelines;
@@ -162,6 +168,11 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
container_of(me->parent, struct fimc_md, media_dev);
}
+static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct fimc_md, subdev_notifier);
+}
+
static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
{
mutex_lock(&ve->vdev.entity.parent->graph_mutex);
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 09/10] ARM: dts: Add rear camera nodes for Exynos4412 TRATS2 board
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
This patch enables the rear facing camera (s5c73m3) on TRATS2 board
by adding the I2C0 bus controller, s5c73m3 sensor, MIPI CSI-2 receiver
and the sensor's voltage regulator supply nodes.
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
arch/arm/boot/dts/exynos4412-trats2.dts | 86 +++++++++++++++++++++++++++++--
1 file changed, 81 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 4f851cc..d7e13a4 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -71,7 +71,33 @@
enable-active-high;
};
- /* More to come */
+ cam_af_reg: voltage-regulator-2 {
+ compatible = "regulator-fixed";
+ regulator-name = "CAM_AF";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpm0 4 0>;
+ enable-active-high;
+ };
+
+ cam_isp_core_reg: voltage-regulator-3 {
+ compatible = "regulator-fixed";
+ regulator-name = "CAM_ISP_CORE_1.2V_EN";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ gpio = <&gpm0 3 0>;
+ enable-active-high;
+ regulator-always-on;
+ };
+
+ lcd_vdd3_reg: voltage-regulator-4 {
+ compatible = "regulator-fixed";
+ regulator-name = "LCD_VDD_2.2V";
+ regulator-min-microvolt = <2200000>;
+ regulator-max-microvolt = <2200000>;
+ gpio = <&gpc0 1 0>;
+ enable-active-high;
+ };
};
gpio-keys {
@@ -106,6 +132,38 @@
};
};
+ i2c_0: i2c@13860000 {
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-slave-addr = <0x10>;
+ samsung,i2c-max-bus-freq = <400000>;
+ pinctrl-0 = <&i2c0_bus>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ s5c73m3@3c {
+ compatible = "samsung,s5c73m3";
+ reg = <0x3c>;
+ standby-gpios = <&gpm0 1 1>; /* ISP_STANDBY */
+ xshutdown-gpios = <&gpf1 3 1>; /* ISP_RESET */
+ vdd-int-supply = <&buck9_reg>;
+ vddio-cis-supply = <&ldo9_reg>;
+ vdda-supply = <&ldo17_reg>;
+ vddio-host-supply = <&ldo18_reg>;
+ vdd-af-supply = <&cam_af_reg>;
+ vdd-reg-supply = <&cam_io_reg>;
+ clock-frequency = <24000000>;
+ /* CAM_A_CLKOUT */
+ clocks = <&camera 0>;
+ clock-names = "cis_extclk";
+ port {
+ s5c73m3_ep: endpoint {
+ remote-endpoint = <&csis0_ep>;
+ data-lanes = <1 2 3 4>;
+ };
+ };
+ };
+ };
+
i2c@13890000 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-slave-addr = <0x10>;
@@ -511,8 +569,8 @@
};
};
- camera {
- pinctrl-0 = <&cam_port_b_clk_active>;
+ camera: camera {
+ pinctrl-0 = <&cam_port_a_clk_active &cam_port_b_clk_active>;
pinctrl-names = "default";
status = "okay";
@@ -532,6 +590,23 @@
status = "okay";
};
+ csis_0: csis@11880000 {
+ status = "okay";
+ vddcore-supply = <&ldo8_reg>;
+ vddio-supply = <&ldo10_reg>;
+ clock-frequency = <176000000>;
+
+ /* Camera C (3) MIPI CSI-2 (CSIS0) */
+ port@3 {
+ reg = <3>;
+ csis0_ep: endpoint {
+ remote-endpoint = <&s5c73m3_ep>;
+ data-lanes = <1 2 3 4>;
+ samsung,csis-hs-settle = <12>;
+ };
+ };
+ };
+
csis_1: csis@11890000 {
vddcore-supply = <&ldo8_reg>;
vddio-supply = <&ldo10_reg>;
@@ -572,10 +647,11 @@
reg = <0x10>;
svdda-supply = <&cam_io_reg>;
svddio-supply = <&ldo19_reg>;
+ afvdd-supply = <&ldo19_reg>;
clock-frequency = <24000000>;
/* CAM_B_CLKOUT */
- clocks = <&clock_cam 1>;
- clock-names = "mclk";
+ clocks = <&camera 1>;
+ clock-names = "extclk";
samsung,camclk-out = <1>;
gpios = <&gpm1 6 0>;
--
1.7.9.5
^ permalink raw reply related
* [PATCH v4 10/10] ARM: dts: exynos4: Update clk provider part of the camera subsystem
From: Sylwester Nawrocki @ 2014-02-20 19:40 UTC (permalink / raw)
To: linux-media, devicetree
Cc: linux-samsung-soc, linux-arm-kernel, robh+dt, mark.rutland, galak,
kyungmin.park, kgene.kim, a.hajda, Sylwester Nawrocki
In-Reply-To: <1392925237-31394-1-git-send-email-s.nawrocki@samsung.com>
Remove unused /camera/clock-controller node and required clock properties
to the camera node. This is required for a clock provider that will be
referenced by image sensor devices.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
arch/arm/boot/dts/exynos4.dtsi | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 08452e1..d1ee347 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -109,12 +109,10 @@
status = "disabled";
#address-cells = <1>;
#size-cells = <1>;
+ #clock-cells = <1>;
+ clock-output-names = "cam_mclk_a", "cam_mclk_b";
ranges;
- clock_cam: clock-controller {
- #clock-cells = <1>;
- };
-
fimc_0: fimc@11800000 {
compatible = "samsung,exynos4210-fimc";
reg = <0x11800000 0x1000>;
--
1.7.9.5
^ permalink raw reply related
* Re: devicetree repository separation/migration
From: Frank Rowand @ 2014-02-20 21:15 UTC (permalink / raw)
To: Grant Likely
Cc: Sascha Hauer, Tim Bird, Olof Johansson, Jason Cooper, Rob Herring,
Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala, Rob Landley,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-spec-u79uwXL29TY76Z2rM5mHXA,
devicetree-compiler-u79uwXL29TY76Z2rM5mHXA, Ian Campbell
In-Reply-To: <CACxGe6ugF2_kzDPLdv-Z1LUnkX4DHq9R45o64kJHVBSQjWFiNg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 2/20/2014 12:14 AM, Grant Likely wrote:
> On Thu, Feb 20, 2014 at 4:58 AM, Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On 2/19/2014 1:15 PM, Grant Likely wrote:
>>> On Wed, Feb 19, 2014 at 8:16 PM, Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>>> On 2/19/2014 1:08 AM, Sascha Hauer wrote:
>>>>> On Tue, Feb 18, 2014 at 02:44:15PM -0800, Tim Bird wrote:
>>>>>> I'm not in favor of separating the device tree information from the kernel.
>>>>>>
>>>>>> If we switch, then whatever synchronization issues other projects
>>>>>> are having now with synching with the device tree info from the kernel will
>>>>>> just then become the problem of the kernel developers, who will then
>>>>>> have to sync with the device tree info from another repository. If the
>>>>>> sync issues can't be solved now for them, why or how would it be solved
>>>>>> post-separation for us? (It sounds like a zero-sum game of pain transfer
>>>>>> to me.)
>>>>>>
>>>>>> I'm relatively unfamiliar with the arguments. Can someone provide
>>>>>> a brief list of reasons this is needed, and how the inconvenience to Linux
>>>>>> kernel developers will be minimized, should it proceed?
>>>>>
>>>>
>>>>
>>>>> One of the reasons for doing devicetrees is to separate the hardware
>>>>> description from the code so that:
>>>>> - Other OSes (and bootloaders) can use the same description to start on
>>>>> a given hardware
>>>>> - A generic Kernel can be started on any hardware
>>>>> - A hardware describes itself, makes itself more introspecitve so we can
>>>>> go away from very specialized kernels
>>>>
>>>> Tim knows this ^^^^. He was asking for the arguments for moving dts files
>>>> out of the linux kernel source tree.
>>>
>>> We've made the decision that devicetree bindings need to be treated as
>>> ABI, but as long as the .dts files live in the kernel there will
>>> always be the temptation to just tweak things in lock-step and nobody
>>> will notice. Splitting the files out gives that extra push to think
>>> about whether changes to a binding will backwards compatible with a
>>> tree that doesn't have those changes because the chances are a lot
>>> higher that someone will hit that combination.
>>>
>>> The other argument is shared source between
>>> BSD/U-Boot/Barebox/Linux/etc. Until we have a separate .dts repo there
>>> is no good way to share the database of hardware descriptions.
>>
>> We could provide an easy export (see below). What do you think?
>
> Ian Campbell is already maintaining an export tree as a staging area
> for an eventual split. He's had it up and running for almost a year
> now:
>
> http://xenbits.xen.org/gitweb/?p=people/ianc/device-tree-rebasing.git
>
> g.
>
So there already is a "good way to share the database of hardware
descriptions" in addition to the one I provided.
-Frank
--
To unsubscribe from this list: send the line "unsubscribe devicetree-spec" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v3 0/2] Qualcomm Universal Peripheral (QUP) I2C controller
From: Bjorn Andersson @ 2014-02-21 0:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Rob Landley, Wolfram Sang, Grant Likely, devicetree, linux-doc,
linux-kernel, linux-i2c, linux-arm-msm, linux-arm-kernel
This third revision of the QUP I2C driver series comes with quite a bit of
cleanup and corrections. Most notably is the removal of the "magic" delay loops
and bug fixes related to larger than 32 byte reads and writes.
Special thanks to Andy Gross for helping answering questions regarding the
inner working of this block.
Regards,
Bjorn
Changes from v3:
- Simplified interrupt handler
- Corrected the state transition poll timeout
- Refactored state transition code
- Refactored the polling functions waiting for transfers to finish
- Made the write fifo fill function care if there's space
- Corrected programmed length on writes
- Made block read and block write work
- Removed data duplicates from qup_i2c_dev
- Changed timeout to HZ, to give room for clock stretching
- Properly reject reads over 256 bytes, as limited by HW
- Dropped reinitialization of completions
- Made sure to not re-initiate reads for every block read
- Added QUP version number to compatible
Changes from v2:
- Removed unused variables and includes
- Corrected read logic in irq handler
- Made the polling loop in qup_i2c_poll_state() less arbitrary
- Only building suspend/resume if CONFIG_PM_SLEEP
Changes from v1:
- Cleaned up device tree binding example.
- Refrased device tree bindings.
- Following changes in the i2c framework.
- Use the core clock to calculate divider for the bus clock, instead of
explicitly setting it.
- Remove explicit pinctrl settting.
- Split/renamed qup_i2c_enable(bool) into enable/disable functions.
- Return value was overwritten on error in write_one/read_one.
- Initialize the i2c core every time, so that we actually can execute
more than 1 transmission per xfer.
Bjorn Andersson (1):
i2c: New bus driver for the Qualcomm QUP I2C controller
Ivan T. Ivanov (1):
i2c: qup: Add device tree bindings information
.../devicetree/bindings/i2c/qcom,i2c-qup.txt | 44 ++
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-qup.c | 772 ++++++++++++++++++++
4 files changed, 827 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
create mode 100644 drivers/i2c/busses/i2c-qup.c
--
1.7.9.5
^ permalink raw reply
* [PATCH v3 1/2] i2c: qup: Add device tree bindings information
From: Bjorn Andersson @ 2014-02-21 0:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Rob Landley, Wolfram Sang, Grant Likely,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Ivan T. Ivanov
In-Reply-To: <1392943090-30556-1-git-send-email-bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
From: "Ivan T. Ivanov" <iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
The Qualcomm Universal Peripherial (QUP) wraps I2C mini-core and
provide input and output FIFO's for it. I2C controller can operate
as master with supported bus speeds of 100Kbps and 400Kbps.
Signed-off-by: Ivan T. Ivanov <iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
[bjorn: reformulated part of binding description
added version to compatible
cleaned up example]
Signed-off-by: Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
---
.../devicetree/bindings/i2c/qcom,i2c-qup.txt | 44 ++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
new file mode 100644
index 0000000..38d505f
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
@@ -0,0 +1,44 @@
+Qualcomm Universal Peripheral (QUP) I2C controller
+
+Required properties:
+ - compatible: Should be
+ "qcom,i2c-qup-v1.1.1" for 8660, 8960 and 8064.
+ "qcom,i2c-qup-v2.1.1" for 8974 v1.
+ "qcom,i2c-qup-v2.2.1" for 8974 v2 and later.
+ - reg: Should contain QUP register address and length.
+ - interrupts: Should contain I2C interrupt.
+
+ - clocks: Should contain the core clock and the AHB clock.
+ - clock-names: Should be "core" for the core clock and "iface" for the
+ AHB clock.
+
+ - #address-cells: Should be <1> Address cells for i2c device address
+ - #size-cells: Should be <0> as i2c addresses have no size component
+
+Optional properties:
+ - clock-frequency: Should specify the desired i2c bus clock frequency in Hz,
+ defaults to 100kHz if omitted.
+
+Child nodes should conform to i2c bus binding.
+
+Example:
+
+ i2c@f9924000 {
+ compatible = "qcom,i2c-qup-v2.2.1";
+ reg = <0xf9924000 0x1000>;
+ interrupts = <0 96 0>;
+
+ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+
+ clock-frequency = <355000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dummy@60 {
+ compatible = "dummy";
+ reg = <0x60>;
+ };
+ };
+
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 2/2] i2c: New bus driver for the Qualcomm QUP I2C controller
From: Bjorn Andersson @ 2014-02-21 0:38 UTC (permalink / raw)
To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Rob Landley, Wolfram Sang, Grant Likely, devicetree, linux-doc,
linux-kernel, linux-i2c, linux-arm-msm, linux-arm-kernel
Cc: Andy Gross, Stephen Boyd, Ivan T. Ivanov
In-Reply-To: <1392943090-30556-1-git-send-email-bjorn.andersson@sonymobile.com>
This bus driver supports the QUP i2c hardware controller in the Qualcomm SOCs.
The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path
engine with input/output FIFOs and an embedded i2c mini-core. The driver
supports FIFO mode (for low bandwidth applications) and block mode (interrupt
generated for each block-size data transfer).
Cc: Andy Gross <agross@codeaurora.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-qup.c | 772 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 783 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-qup.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f5ed031..403e196 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -648,6 +648,16 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
+config I2C_QUP
+ tristate "Qualcomm QUP based I2C controller"
+ depends on ARCH_MSM
+ help
+ If you say yes to this option, support will be included for the
+ built-in I2C interface on the Qualcomm SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-qup.
+
config I2C_RIIC
tristate "Renesas RIIC adapter"
depends on ARCH_SHMOBILE || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a08931f..bf2257b 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
+obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
new file mode 100644
index 0000000..a2dfd57
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+/* QUP Registers */
+#define QUP_CONFIG 0x000
+#define QUP_STATE 0x004
+#define QUP_IO_MODE 0x008
+#define QUP_SW_RESET 0x00c
+#define QUP_OPERATIONAL 0x018
+#define QUP_ERROR_FLAGS 0x01c
+#define QUP_ERROR_FLAGS_EN 0x020
+#define QUP_HW_VERSION 0x030
+#define QUP_MX_OUTPUT_CNT 0x100
+#define QUP_OUT_FIFO_BASE 0x110
+#define QUP_MX_WRITE_CNT 0x150
+#define QUP_MX_INPUT_CNT 0x200
+#define QUP_MX_READ_CNT 0x208
+#define QUP_IN_FIFO_BASE 0x218
+#define QUP_I2C_CLK_CTL 0x400
+#define QUP_I2C_STATUS 0x404
+
+/* QUP States and reset values */
+#define QUP_RESET_STATE 0
+#define QUP_RUN_STATE 1
+#define QUP_PAUSE_STATE 3
+#define QUP_STATE_MASK 3
+
+#define QUP_STATE_VALID BIT(2)
+#define QUP_I2C_MAST_GEN BIT(4)
+
+#define QUP_OPERATIONAL_RESET 0x000ff0
+#define QUP_I2C_STATUS_RESET 0xfffffc
+
+/* QUP OPERATIONAL FLAGS */
+#define QUP_I2C_NACK_FLAG BIT(3)
+#define QUP_OUT_NOT_EMPTY BIT(4)
+#define QUP_IN_NOT_EMPTY BIT(5)
+#define QUP_OUT_FULL BIT(6)
+#define QUP_OUT_SVC_FLAG BIT(8)
+#define QUP_IN_SVC_FLAG BIT(9)
+#define QUP_MX_OUTPUT_DONE BIT(10)
+#define QUP_MX_INPUT_DONE BIT(11)
+
+/* I2C mini core related values */
+#define QUP_CLOCK_AUTO_GATE BIT(13)
+#define I2C_MINI_CORE (2 << 8)
+#define I2C_N_VAL 15
+/* Most significant word offset in FIFO port */
+#define QUP_MSW_SHIFT (I2C_N_VAL + 1)
+
+/* Packing/Unpacking words in FIFOs, and IO modes */
+#define QUP_OUTPUT_BLK_MODE (1 << 10)
+#define QUP_INPUT_BLK_MODE (1 << 12)
+#define QUP_UNPACK_EN BIT(14)
+#define QUP_PACK_EN BIT(15)
+
+#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN)
+
+#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03)
+#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07)
+#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03)
+#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07)
+
+/* QUP tags */
+#define QUP_TAG_START (1 << 8)
+#define QUP_TAG_DATA (2 << 8)
+#define QUP_TAG_STOP (3 << 8)
+#define QUP_TAG_REC (4 << 8)
+
+/* Status, Error flags */
+#define I2C_STATUS_WR_BUFFER_FULL BIT(0)
+#define I2C_STATUS_BUS_ACTIVE BIT(8)
+#define I2C_STATUS_ERROR_MASK 0x38000fc
+#define QUP_STATUS_ERROR_FLAGS 0x7c
+
+#define QUP_READ_LIMIT 256
+
+struct qup_i2c_dev {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ struct clk *pclk;
+ struct i2c_adapter adap;
+
+ int clk_ctl;
+ int out_fifo_sz;
+ int in_fifo_sz;
+ int out_blk_sz;
+ int in_blk_sz;
+
+ unsigned long one_byte_t;
+
+ struct i2c_msg *msg;
+ /* Current posion in user message buffer */
+ int pos;
+ /* I2C protocol errors */
+ u32 bus_err;
+ /* QUP core errors */
+ u32 qup_err;
+
+ struct completion xfer;
+};
+
+static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
+{
+ struct qup_i2c_dev *qup = dev;
+ u32 bus_err;
+ u32 qup_err;
+ u32 opflags;
+
+ bus_err = readl(qup->base + QUP_I2C_STATUS);
+ qup_err = readl(qup->base + QUP_ERROR_FLAGS);
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+
+ if (!qup->msg) {
+ /* Clear Error interrupt */
+ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
+ return IRQ_HANDLED;
+ }
+
+ bus_err &= I2C_STATUS_ERROR_MASK;
+ qup_err &= QUP_STATUS_ERROR_FLAGS;
+
+ if (qup_err) {
+ /* Clear Error interrupt */
+ writel(qup_err, qup->base + QUP_ERROR_FLAGS);
+ goto done;
+ }
+
+ if (bus_err) {
+ /* Clear Error interrupt */
+ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
+ goto done;
+ }
+
+ if (opflags & QUP_IN_SVC_FLAG)
+ writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+ if (opflags & QUP_OUT_SVC_FLAG)
+ writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+done:
+ qup->qup_err = qup_err;
+ qup->bus_err = bus_err;
+ complete(&qup->xfer);
+ return IRQ_HANDLED;
+}
+
+static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup,
+ u32 req_state, u32 req_mask)
+{
+ int retries = 1;
+ u32 state;
+
+ /*
+ * State transition takes 3 AHB clocks cycles + 3 I2C master clock
+ * cycles. So retry once after a 1uS delay.
+ */
+ do {
+ state = readl(qup->base + QUP_STATE);
+
+ if (state & QUP_STATE_VALID &&
+ (state & req_mask) == req_state)
+ return 0;
+
+ udelay(1);
+ } while (retries--);
+
+ return -ETIMEDOUT;
+}
+
+static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state)
+{
+ return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
+}
+
+static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup)
+{
+ return qup_i2c_poll_state_mask(qup, 0, 0);
+}
+
+static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup)
+{
+ return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
+}
+
+static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
+{
+ if (qup_i2c_poll_state_valid(qup) != 0)
+ return -EIO;
+
+ writel(state, qup->base + QUP_STATE);
+
+ if (qup_i2c_poll_state(qup, state) != 0)
+ return -EIO;
+ return 0;
+}
+
+static int qup_i2c_wait_writeready(struct qup_i2c_dev *qup)
+{
+ unsigned long timeout;
+ u32 opflags;
+ u32 status;
+
+ timeout = jiffies + HZ;
+
+ for (;;) {
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+ status = readl(qup->base + QUP_I2C_STATUS);
+
+ if (!(opflags & QUP_OUT_NOT_EMPTY) &&
+ !(status & I2C_STATUS_BUS_ACTIVE))
+ return 0;
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ usleep_range(qup->one_byte_t, qup->one_byte_t * 2);
+ }
+}
+
+static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ /* Number of entries to shift out, including the start */
+ int total = msg->len + 1;
+
+ if (total < qup->out_fifo_sz) {
+ /* FIFO mode */
+ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
+ writel(total, qup->base + QUP_MX_WRITE_CNT);
+ } else {
+ /* BLOCK mode (transfer data on chunks) */
+ writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
+ qup->base + QUP_IO_MODE);
+ writel(total, qup->base + QUP_MX_OUTPUT_CNT);
+ }
+}
+
+static void qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ u32 addr = msg->addr << 1;
+ u32 qup_tag;
+ u32 opflags;
+ int idx;
+ u32 val;
+
+ if (qup->pos == 0) {
+ val = QUP_TAG_START | addr;
+ idx = 1;
+ } else {
+ val = 0;
+ idx = 0;
+ }
+
+ while (qup->pos < msg->len) {
+ /* Check that there's space in the FIFO for our pair */
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+ if (opflags & QUP_OUT_FULL)
+ break;
+
+ if (qup->pos == msg->len - 1)
+ qup_tag = QUP_TAG_STOP;
+ else
+ qup_tag = QUP_TAG_DATA;
+
+ if (idx & 1)
+ val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT;
+ else
+ val = qup_tag | msg->buf[qup->pos];
+
+ /* Write out the pair and the last odd value */
+ if (idx & 1 || qup->pos == msg->len - 1)
+ writel(val, qup->base + QUP_OUT_FIFO_BASE);
+
+ qup->pos++;
+ idx++;
+ }
+}
+
+static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ unsigned long left;
+ int ret;
+
+ qup->msg = msg;
+ qup->pos = 0;
+
+ enable_irq(qup->irq);
+
+ qup_i2c_set_write_mode(qup, msg);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+
+ do {
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ goto err;
+
+ qup_i2c_issue_write(qup, msg);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ left = wait_for_completion_timeout(&qup->xfer, HZ);
+ if (!left) {
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (qup->bus_err || qup->qup_err) {
+ if (qup->bus_err & QUP_I2C_NACK_FLAG)
+ dev_err(qup->dev, "NACK from %x\n", msg->addr);
+ ret = -EIO;
+ goto err;
+ }
+ } while (qup->pos < msg->len);
+
+ /* Wait for the outstanding data in the fifo to drain */
+ ret = qup_i2c_wait_writeready(qup);
+
+err:
+ disable_irq(qup->irq);
+ qup->msg = NULL;
+
+ return ret;
+}
+
+static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
+{
+ if (len < qup->in_fifo_sz) {
+ /* FIFO mode */
+ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
+ writel(len, qup->base + QUP_MX_READ_CNT);
+ } else {
+ /* BLOCK mode (transfer data on chunks) */
+ writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
+ qup->base + QUP_IO_MODE);
+ writel(len, qup->base + QUP_MX_INPUT_CNT);
+ }
+}
+
+static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ u32 addr, len, val;
+
+ addr = (msg->addr << 1) | 1;
+
+ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
+ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
+
+ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
+ writel(val, qup->base + QUP_OUT_FIFO_BASE);
+}
+
+
+static void qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ u32 opflags;
+ u32 val = 0;
+ int idx;
+
+ for (idx = 0; qup->pos < msg->len; idx++) {
+ if ((idx & 1) == 0) {
+ /* Check that FIFO have data */
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+ if (!(opflags & QUP_IN_NOT_EMPTY))
+ break;
+
+ /* Reading 2 words at time */
+ val = readl(qup->base + QUP_IN_FIFO_BASE);
+
+ msg->buf[qup->pos++] = val & 0xFF;
+ } else {
+ msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
+ }
+ }
+}
+
+static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ unsigned long left;
+ int ret;
+
+ /*
+ * The QUP block will issue a NACK and STOP on the bus when reaching
+ * the end of the read, the length of the read is specified as one byte
+ * which limits the possible read to 256 (QUP_READ_LIMIT) bytes.
+ */
+ if (msg->len > QUP_READ_LIMIT) {
+ dev_err(qup->dev, "HW not capable of reads over %d bytes\n",
+ QUP_READ_LIMIT);
+ return -EINVAL;
+ }
+
+ qup->msg = msg;
+ qup->pos = 0;
+
+ enable_irq(qup->irq);
+
+ qup_i2c_set_read_mode(qup, msg->len);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ goto err;
+
+ qup_i2c_issue_read(qup, msg);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ do {
+ left = wait_for_completion_timeout(&qup->xfer, HZ);
+ if (!left) {
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (qup->bus_err || qup->qup_err) {
+ if (qup->bus_err & QUP_I2C_NACK_FLAG)
+ dev_err(qup->dev, "NACK from %x\n", msg->addr);
+ ret = -EIO;
+ goto err;
+ }
+
+ qup_i2c_read_fifo(qup, msg);
+ } while (qup->pos < msg->len);
+
+err:
+ disable_irq(qup->irq);
+ qup->msg = NULL;
+
+ return ret;
+}
+
+static int qup_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
+ int ret, idx;
+
+ ret = pm_runtime_get_sync(qup->dev);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
+ if (ret)
+ goto out;
+
+ /* Configure QUP as I2C mini core */
+ writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
+
+ for (idx = 0; idx < num; idx++) {
+ if (msgs[idx].len == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (qup_i2c_poll_state_i2c_master(qup)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (msgs[idx].flags & I2C_M_RD)
+ ret = qup_i2c_read_one(qup, &msgs[idx]);
+ else
+ ret = qup_i2c_write_one(qup, &msgs[idx]);
+
+ if (ret)
+ break;
+
+ ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
+ if (ret)
+ break;
+ }
+
+ if (ret == 0)
+ ret = num;
+out:
+
+ pm_runtime_mark_last_busy(qup->dev);
+ pm_runtime_put_autosuspend(qup->dev);
+
+ return ret;
+}
+
+static u32 qup_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm qup_i2c_algo = {
+ .master_xfer = qup_i2c_xfer,
+ .functionality = qup_i2c_func,
+};
+
+static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
+{
+ clk_prepare_enable(qup->clk);
+ clk_prepare_enable(qup->pclk);
+}
+
+static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
+{
+ u32 config;
+
+ qup_i2c_change_state(qup, QUP_RESET_STATE);
+ clk_disable_unprepare(qup->clk);
+ config = readl(qup->base + QUP_CONFIG);
+ config |= QUP_CLOCK_AUTO_GATE;
+ writel(config, qup->base + QUP_CONFIG);
+ clk_disable_unprepare(qup->pclk);
+}
+
+static int qup_i2c_probe(struct platform_device *pdev)
+{
+ static const int blk_sizes[] = {4, 16, 32};
+ struct device_node *node = pdev->dev.of_node;
+ struct qup_i2c_dev *qup;
+ unsigned long one_bit_t;
+ struct resource *res;
+ u32 val, io_mode, hw_ver, size;
+ int ret, fs_div, hs_div;
+ int src_clk_freq;
+ int clk_freq;
+
+ qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
+ if (!qup)
+ return -ENOMEM;
+
+ qup->dev = &pdev->dev;
+ init_completion(&qup->xfer);
+ platform_set_drvdata(pdev, qup);
+
+ clk_freq = 100000;
+ if (!of_property_read_u32(node, "clock-frequency", &val))
+ clk_freq = val;
+
+ /* We support frequencies up to FAST Mode (400KHz) */
+ if (clk_freq <= 0 || clk_freq > 400000) {
+ dev_err(qup->dev, "clock frequency not supported %d\n",
+ clk_freq);
+ return -EIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ qup->base = devm_ioremap_resource(qup->dev, res);
+ if (IS_ERR(qup->base))
+ return PTR_ERR(qup->base);
+
+ qup->irq = platform_get_irq(pdev, 0);
+ if (qup->irq < 0) {
+ dev_err(qup->dev, "No IRQ defined\n");
+ return qup->irq;
+ }
+
+ qup->clk = devm_clk_get(qup->dev, "core");
+ if (IS_ERR(qup->clk)) {
+ dev_err(qup->dev, "Could not get core clock\n");
+ return PTR_ERR(qup->clk);
+ }
+
+ qup->pclk = devm_clk_get(qup->dev, "iface");
+ if (IS_ERR(qup->pclk)) {
+ dev_err(qup->dev, "Could not get iface clock\n");
+ return PTR_ERR(qup->pclk);
+ }
+
+ qup_i2c_enable_clocks(qup);
+
+ /*
+ * Bootloaders might leave a pending interrupt on certain QUP's,
+ * so we reset the core before registering for interrupts.
+ */
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = qup_i2c_poll_state_valid(qup);
+ if (ret)
+ goto fail;
+
+ ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt,
+ IRQF_TRIGGER_HIGH, "i2c_qup", qup);
+ if (ret) {
+ dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq);
+ goto fail;
+ }
+ disable_irq(qup->irq);
+
+ hw_ver = readl(qup->base + QUP_HW_VERSION);
+ dev_dbg(qup->dev, "Revision %x\n", hw_ver);
+
+ io_mode = readl(qup->base + QUP_IO_MODE);
+
+ /*
+ * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
+ * associated with each byte written/received
+ */
+ size = QUP_OUTPUT_BLOCK_SIZE(io_mode);
+ if (size > ARRAY_SIZE(blk_sizes))
+ return -EIO;
+ qup->out_blk_sz = blk_sizes[size] / 2;
+
+ size = QUP_INPUT_BLOCK_SIZE(io_mode);
+ if (size > ARRAY_SIZE(blk_sizes))
+ return -EIO;
+ qup->in_blk_sz = blk_sizes[size] / 2;
+
+ size = QUP_OUTPUT_FIFO_SIZE(io_mode);
+ qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
+
+ size = QUP_INPUT_FIFO_SIZE(io_mode);
+ qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
+
+ src_clk_freq = clk_get_rate(qup->clk);
+ fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
+ hs_div = 3;
+ qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
+
+ /*
+ * Time it takes for a byte to be clocked out on the bus.
+ * Each byte takes 9 clock cycles (8 bits + 1 ack).
+ */
+ one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
+ qup->one_byte_t = one_bit_t * 9;
+
+ dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
+ qup->in_blk_sz, qup->in_fifo_sz,
+ qup->out_blk_sz, qup->out_fifo_sz);
+
+ i2c_set_adapdata(&qup->adap, qup);
+ qup->adap.algo = &qup_i2c_algo;
+ qup->adap.nr = pdev->id;
+ qup->adap.dev.parent = qup->dev;
+ qup->adap.dev.of_node = pdev->dev.of_node;
+ strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
+
+ ret = i2c_add_numbered_adapter(&qup->adap);
+ if (ret)
+ goto fail;
+
+ pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(qup->dev);
+ pm_runtime_enable(qup->dev);
+ return 0;
+
+fail:
+ qup_i2c_disable_clocks(qup);
+ return ret;
+}
+
+static int qup_i2c_remove(struct platform_device *pdev)
+{
+ struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
+
+ disable_irq(qup->irq);
+ qup_i2c_disable_clocks(qup);
+ i2c_del_adapter(&qup->adap);
+ pm_runtime_disable(qup->dev);
+ pm_runtime_set_suspended(qup->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int qup_i2c_pm_suspend_runtime(struct device *device)
+{
+ struct qup_i2c_dev *qup = dev_get_drvdata(device);
+
+ dev_dbg(device, "pm_runtime: suspending...\n");
+ qup_i2c_disable_clocks(qup);
+ return 0;
+}
+
+static int qup_i2c_pm_resume_runtime(struct device *device)
+{
+ struct qup_i2c_dev *qup = dev_get_drvdata(device);
+
+ dev_dbg(device, "pm_runtime: resuming...\n");
+ qup_i2c_enable_clocks(qup);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int qup_i2c_suspend(struct device *device)
+{
+ dev_dbg(device, "system suspend");
+ qup_i2c_pm_suspend_runtime(device);
+ return 0;
+}
+
+static int qup_i2c_resume(struct device *device)
+{
+ dev_dbg(device, "system resume");
+ qup_i2c_pm_resume_runtime(device);
+ pm_runtime_mark_last_busy(device);
+ pm_request_autosuspend(device);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops qup_i2c_qup_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(
+ qup_i2c_suspend,
+ qup_i2c_resume)
+ SET_RUNTIME_PM_OPS(
+ qup_i2c_pm_suspend_runtime,
+ qup_i2c_pm_resume_runtime,
+ NULL)
+};
+
+static const struct of_device_id qup_i2c_dt_match[] = {
+ { .compatible = "qcom,i2c-qup-v1.1.1" },
+ { .compatible = "qcom,i2c-qup-v2.1.1" },
+ { .compatible = "qcom,i2c-qup-v2.2.1" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
+
+static struct platform_driver qup_i2c_driver = {
+ .probe = qup_i2c_probe,
+ .remove = qup_i2c_remove,
+ .driver = {
+ .name = "i2c_qup",
+ .owner = THIS_MODULE,
+ .pm = &qup_i2c_qup_pm_ops,
+ .of_match_table = qup_i2c_dt_match,
+ },
+};
+
+module_platform_driver(qup_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c_qup");
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH v6 0/8] ARM: brcmstb: Add Broadcom STB SoC support
From: Florian Fainelli @ 2014-02-21 0:41 UTC (permalink / raw)
To: Marc Carino
Cc: Christian Daudt, Arnd Bergmann, Olof Johansson, Matt Porter,
Russell King, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
In-Reply-To: <1391463041-15241-1-git-send-email-marc.ceeeee@gmail.com>
Matt,
2014-02-03 13:30 GMT-08:00 Marc Carino <marc.ceeeee@gmail.com>:
> This patchset contains the board support package for the
> Broadcom BCM7445 ARM-based SoC [1]. These changes contain a
> minimal set of code needed for a BCM7445-based board to boot
> the Linux kernel.
If you are happy with this round of changes, can you also carry those
patches in your tree for 3.15?
>
> These changes heavily leverage the OF/devicetree framework.
>
> v6:
> - rebased to v3.14-rc1
> - utilize common APIs for handling CPU power-down
> - drop deprecated __cpuinit attributes
>
> v5 (https://lkml.org/lkml/2014/1/21/640):
> - rebased to v3.13 tag
> - make UART DT node a child of 'rdb' node
> - fix ordering of debug UART entries
>
> v4 (https://lkml.org/lkml/2014/1/17/455):
> - make a reboot driver and put it in the drivers folder
> - rework DT bindings to leverage 'syscon'
> - rework BSP code to use 'syscon' for all register mappings
> - misc. tweaks per suggestions from v3
>
> v3 (https://lkml.org/lkml/2014/1/14/696):
> - rebased to v3.13-rc8
> - switched to using 'multi_v7_defconfig'
> - eliminated dependence on compile-time peripheral register access
> - moved DT node iomap out from 'init_early'
> - misc. minor cleanups from mailing-list discussion for v2
>
> v2 (https://lkml.org/lkml/2013/11/26/570):
> - rebased to v3.13-rc1
> - moved implementation to 'mach-bcm' folder
> - added CPU init for B16RM
>
> v1:
> - initial submission
>
> [1] http://www.broadcom.com/products/Cable/Cable-Set-Top-Box-Solutions/BCM7445
>
> Marc Carino (8):
> ARM: brcmstb: add infrastructure for ARM-based Broadcom STB SoCs
> power: reset: Add reboot driver for brcmstb
> ARM: brcmstb: add debug UART for earlyprintk support
> ARM: do CPU-specific init for Broadcom Brahma15 cores
> ARM: brcmstb: add CPU binding for Broadcom Brahma15
> ARM: brcmstb: add misc. DT bindings for brcmstb
> ARM: brcmstb: gic: add compatible string for Broadcom Brahma15
> ARM: brcmstb: dts: add a reference DTS for Broadcom 7445
>
> .../devicetree/bindings/arm/brcm-brcmstb.txt | 95 ++++++
> Documentation/devicetree/bindings/arm/cpus.txt | 1 +
> Documentation/devicetree/bindings/arm/gic.txt | 1 +
> arch/arm/Kconfig.debug | 15 +-
> arch/arm/boot/dts/bcm7445.dts | 111 +++++++
> arch/arm/configs/multi_v7_defconfig | 1 +
> arch/arm/mach-bcm/Kconfig | 15 +
> arch/arm/mach-bcm/Makefile | 4 +
> arch/arm/mach-bcm/brcmstb.c | 104 +++++++
> arch/arm/mach-bcm/brcmstb.h | 38 +++
> arch/arm/mach-bcm/headsmp-brcmstb.S | 33 ++
> arch/arm/mach-bcm/hotplug-brcmstb.c | 311 ++++++++++++++++++++
> arch/arm/mm/proc-v7.S | 11 +
> drivers/power/reset/Kconfig | 10 +
> drivers/power/reset/Makefile | 1 +
> drivers/power/reset/brcmstb-reboot.c | 120 ++++++++
> 16 files changed, 870 insertions(+), 1 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/arm/brcm-brcmstb.txt
> create mode 100644 arch/arm/boot/dts/bcm7445.dts
> create mode 100644 arch/arm/mach-bcm/brcmstb.c
> create mode 100644 arch/arm/mach-bcm/brcmstb.h
> create mode 100644 arch/arm/mach-bcm/headsmp-brcmstb.S
> create mode 100644 arch/arm/mach-bcm/hotplug-brcmstb.c
> create mode 100644 drivers/power/reset/brcmstb-reboot.c
>
--
Florian
^ permalink raw reply
* Re: [PATCH 1/3] NFC: trf7970a: Add driver with ISO/IEC 14443 Type 2 Tag Support
From: Samuel Ortiz @ 2014-02-21 0:50 UTC (permalink / raw)
To: Mark A. Greer
Cc: Lauro Ramos Venancio, Aloisio Almeida Jr, Felipe Balbi,
Erick Macias, Thierry Escande,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1391206631-9862-2-git-send-email-mgreer-luAo+O/VEmrlveNOaEYElw@public.gmane.org>
Hi Mark,
Code looks quite nice, especially since this looks like a fairly complex
driver. And the initial coments are quite useful, I appreciate that.
I have a few comments/questions though:
> + * been received and there isn't an error). The delay is 3 ms since delays
> + * over 2 ms have been observed during testing.
Would you say this timeout depend on the SPI bus bandwidth ?
> +#define TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA BIT(0)
Are there actually any TRF970 devices that do not need this quirk ?
> +static void trf7970a_drain_fifo(struct trf7970a *trf, u8 status)
> +{
> + struct sk_buff *skb = trf->rx_skb;
> + int ret;
> + u8 fifo_bytes;
> +
> + if (status & TRF7970A_IRQ_STATUS_ERROR) {
> + trf7970a_abort_and_send_err(trf, -EIO);
> + return;
> + }
> +
> + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes);
> + if (ret) {
> + trf7970a_abort_and_send_err(trf, ret);
> + return;
> + }
> +
> + dev_dbg(trf->dev, "fifo_bytes: 0x%x\n", fifo_bytes);
> +
> + if (!fifo_bytes)
> + goto no_rx_data;
> +
> + if (fifo_bytes & TRF7970A_FIFO_STATUS_OVERFLOW) {
> + dev_err(trf->dev, "%s - fifo overflow: 0x%x\n", __func__,
> + fifo_bytes);
> + trf7970a_abort_and_send_err(trf, -EIO);
> + return;
> + }
> +
> + if (fifo_bytes > skb_tailroom(skb)) {
> + skb = skb_copy_expand(skb, skb_headroom(skb),
> + max_t(int, fifo_bytes,
> + TRF7970A_RX_SKB_ALLOC_SIZE),
> + GFP_KERNEL);
So there could be more pending bytes in the FIFO than you can accomodate
in your rx_skb ? Could we avoid that by allocating rx_skb to match the
FIFO size ?
> +static void trf7970a_timeout_work_handler(struct work_struct *work)
> +{
> + struct trf7970a *trf = container_of(work, struct trf7970a,
> + timeout_work.work);
> +
> + dev_dbg(trf->dev, "TIMEOUT - state: %d, ignore_timeout: %d\n",
> + trf->state, trf->ignore_timeout);
> +
> + mutex_lock(&trf->lock);
> +
> + if (trf->ignore_timeout)
> + trf->ignore_timeout = false;
> + else if (trf->state == TRF7970A_ST_WAIT_FOR_RX_DATA_CONT)
> + trf7970a_send_upstream(trf); /* No more rx data so send up */
> + else
> + trf7970a_abort_and_send_err(trf, -ETIMEDOUT);
> +
> + mutex_unlock(&trf->lock);
> +}
> +
> +/* ----------------------------------------------------------------- */
Nitpick: I suppose you're separating the internal logic from the digital
ops here ? Please add one comment line for that.
> +static int trf7970a_in_send_cmd(struct nfc_digital_dev *ddev,
> + struct sk_buff *skb, u16 timeout,
> + nfc_digital_cmd_complete_t cb, void *arg)
> +{
> + struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
> + char *prefix;
> + unsigned int len;
> + int ret;
> +
> + dev_dbg(trf->dev, "New request - state: %d, timeout: %d ms, len: %d\n",
> + trf->state, timeout, skb->len);
> +
> + if (skb->len > TRF7970A_TX_MAX)
> + return -EINVAL;
> +
> + mutex_lock(&trf->lock);
> +
> + if ((trf->state != TRF7970A_ST_IDLE) &&
> + (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) {
> + dev_err(trf->dev, "%s - Bogus state: %d\n", __func__,
> + trf->state);
> + ret = -EIO;
> + goto out_err;
> + }
> +
> + trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE,
> + GFP_KERNEL);
> + if (!trf->rx_skb) {
> + dev_dbg(trf->dev, "Can't alloc rx_skb\n");
> + ret = -ENOMEM;
> + goto out_err;
> + }
> +
> + if (trf->state == TRF7970A_ST_IDLE_RX_BLOCKED) {
> + ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX);
> + if (ret)
> + goto out_err;
> +
> + trf->state = TRF7970A_ST_IDLE;
> + }
> +
> + ret = trf7970a_per_cmd_config(trf, skb);
> + if (ret)
> + goto out_err;
> +
> + trf->ddev = ddev;
> + trf->tx_skb = skb;
As you're going to carry this guy around and may need it from e.g. your
threaded interrupt handler, shouldn't you take a reference (skb_get) on it ?
I'm concerned by the fact that you could see your tx_skb disappear from
abort_cmd and get an IRQ before your state is set to IDLE.
Hmm, I guess that's protected by the mutex and so when you get an abort
from the digital stack you reset the state to IDLE and no one should try
to touch tx_skb after you release the mutex. Is that what you had in
mind ?
> +static int trf7970a_probe(struct spi_device *spi)
> +{
> + struct device_node *np = spi->dev.of_node;
> + const struct spi_device_id *id = spi_get_device_id(spi);
> + struct trf7970a *trf;
> + int ret;
> +
> + if (!np) {
> + dev_err(&spi->dev, "No Device Tree entry\n");
> + return -EINVAL;
> + }
> +
> + trf = devm_kzalloc(&spi->dev, sizeof(*trf), GFP_KERNEL);
> + if (!trf)
> + return -ENOMEM;
> +
> + trf->state = TRF7970A_ST_OFF;
> + trf->dev = &spi->dev;
> + trf->spi = spi;
> + trf->quirks = id->driver_data;
> + trf->initialized = false;
> +
> + spi->mode = SPI_MODE_1;
> + spi->bits_per_word = 8;
> +
> + /* Get the optional Slave Select GPIO used for SPI with SS mode */
> + trf->ss_gpio = of_get_named_gpio(np, "ti,ss-gpio", 0);
> + if (trf->ss_gpio >= 0) {
> + ret = devm_gpio_request_one(trf->dev, trf->ss_gpio,
> + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "SS");
> + if (ret) {
> + dev_err(trf->dev, "Can't request SS GPIO: %d\n", ret);
> + return ret;
> + }
> + } else {
> + dev_info(trf->dev, "Using SPI without SS mode\n");
> + }
> +
> + /* There are two enable pins - both must be present */
> + trf->en_gpio = of_get_named_gpio(np, "ti,enable-gpios", 0);
> + if (trf->en_gpio < 0) {
> + dev_err(trf->dev, "No EN GPIO property\n");
> + return ret;
You probably want to return trf->en_gpio or otherwise the devm code
won't release your managed resources.
> + }
> +
> + ret = devm_gpio_request_one(trf->dev, trf->en_gpio,
> + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "EN");
> + if (ret) {
> + dev_err(trf->dev, "Can't request EN GPIO: %d\n", ret);
> + return ret;
> + }
> +
> + trf->en2_gpio = of_get_named_gpio(np, "ti,enable-gpios", 1);
> + if (trf->en2_gpio < 0) {
> + dev_err(trf->dev, "No EN2 GPIO property\n");
> + return ret;
Ditto.
Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 2/2] i2c: New bus driver for the Qualcomm QUP I2C controller
From: Joe Perches @ 2014-02-21 0:52 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Rob Landley, Wolfram Sang, Grant Likely, devicetree, linux-doc,
linux-kernel, linux-i2c, linux-arm-msm, linux-arm-kernel,
Andy Gross, Stephen Boyd, Ivan T. Ivanov
In-Reply-To: <1392943090-30556-3-git-send-email-bjorn.andersson@sonymobile.com>
On Thu, 2014-02-20 at 16:38 -0800, Bjorn Andersson wrote:
> This bus driver supports the QUP i2c hardware controller in the Qualcomm SOCs.
> The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path
> engine with input/output FIFOs and an embedded i2c mini-core. The driver
> supports FIFO mode (for low bandwidth applications) and block mode (interrupt
> generated for each block-size data transfer).
trivia:
> diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
[]
> +#ifdef CONFIG_PM_SLEEP
> +static int qup_i2c_suspend(struct device *device)
> +{
> + dev_dbg(device, "system suspend");
> + qup_i2c_pm_suspend_runtime(device);
> + return 0;
> +}
> +
> +static int qup_i2c_resume(struct device *device)
> +{
> + dev_dbg(device, "system resume");
> + qup_i2c_pm_resume_runtime(device);
> + pm_runtime_mark_last_busy(device);
> + pm_request_autosuspend(device);
> + return 0;
> +}
> +#endif
Missing terminating newlines for dev_dbg statements.
^ permalink raw reply
* Re: [PATCH 2/3] NFC: trf7970a: Add ISO/IEC 15693 Support
From: Samuel Ortiz @ 2014-02-21 1:00 UTC (permalink / raw)
To: Mark A. Greer
Cc: Lauro Ramos Venancio, Aloisio Almeida Jr, Felipe Balbi,
Erick Macias, Thierry Escande,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1391206631-9862-3-git-send-email-mgreer-luAo+O/VEmrlveNOaEYElw@public.gmane.org>
Hi Mark,
Just one nitpick here:
> +static int trf7970a_is_iso15693_write_or_lock(u8 cmd)
> +{
> + int ret;
> +
> + switch (cmd) {
> + case ISO15693_CMD_WRITE_SINGLE_BLOCK:
> + case ISO15693_CMD_LOCK_BLOCK:
> + case ISO15693_CMD_WRITE_MULTIPLE_BLOCK:
> + case ISO15693_CMD_WRITE_AFI:
> + case ISO15693_CMD_LOCK_AFI:
> + case ISO15693_CMD_WRITE_DSFID:
> + case ISO15693_CMD_LOCK_DSFID:
> + ret = 1;
return 1;
and return 0 on the default case.
Simpler code and you don't need the ret variable.
Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 3/3] NFC: trf7970a: Add DTS Documentation
From: Samuel Ortiz @ 2014-02-21 1:04 UTC (permalink / raw)
To: Mark A. Greer
Cc: Lauro Ramos Venancio, Aloisio Almeida Jr, Felipe Balbi,
Erick Macias, Thierry Escande,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-nfc-hn68Rpc1hR1g9hUCZPvPmw,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1391206631-9862-4-git-send-email-mgreer-luAo+O/VEmrlveNOaEYElw@public.gmane.org>
Hi Mark,
On Fri, Jan 31, 2014 at 03:17:11PM -0700, Mark A. Greer wrote:
> Describe the properies used by the trf7970a
> RFID/NFC/15693 transceiver driver.
>
> Signed-off-by: Mark A. Greer <mgreer-luAo+O/VEmrlveNOaEYElw@public.gmane.org>
> ---
> .../devicetree/bindings/net/nfc/trf7970a.txt | 37 ++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/nfc/trf7970a.txt
Not a DT expert, but this looks good to me.
Could you please amend this patch by adding
Documentation/devicetree/bindings/net/nfc/ to the MAINTAINERS NFC
section ?
Cheers,
Samuel.
--
Intel Open Source Technology Centre
http://oss.intel.com/
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: devicetree repository separation/migration
From: Frank Rowand @ 2014-02-21 1:07 UTC (permalink / raw)
To: Jason Cooper, Sascha Hauer
Cc: Grant Likely, Rob Herring, Ian Campbell, pawel.moll-5wv7dgnIgG8,
mark.rutland-5wv7dgnIgG8, galak-sgV2jX0FEOL9JmXXK+q4OQ,
rob-VoJi6FS/r0vR7s880joybQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
devicetree-spec-u79uwXL29TY76Z2rM5mHXA,
devicetree-compiler-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20140217180544.GU7862-u4khhh1J0LxI1Ri9qeTfzeTW4wlIGRCZ@public.gmane.org>
On 2/17/2014 10:05 AM, Jason Cooper wrote:
> All,
>
> At last weeks devicetree irc meeting, I took on the task of writing this
> email. I'm a bit behind.
>
> One of the outcomes of the ARM/devicetree discussion at the 2013 Kernel
> Summit was that we were going to hold off on separating the devicetree
> from the Linux kernel tree. The primary reason for this was to get
> through the backlog of patches.
>
> It's been several months, and we're seeing evidence of other projects
> having difficulty keeping in sync with the kernel tree. Specifically,
> barebox is having trouble syncing:
>
> http://list-archives.org/2014/02/07/barebox-lists-infradead-org/devicetree-maintenance-in-barebox/f/5820726136
< snip >
Sascha,
(Directing this to you, because the devicetree-maintenance-in-barebox thread
begins with an email from you.)
I have read through the referenced thread, but do not yet understand the cause
of the issues the barebox project is facing. What I got from the thread is
that the barebox project maintains some devicetree changes in the project
repository, and it is difficult to manage these changes as the upstream
project (the Linux kernel) makes changes.
What are the barebox changes to dts files?
Why are the changes not submitted upstream? (Or if they were submitted, why
were they not accepted?)
I'm not sure what else to ask to try to understand the issues for barebox. Is
there anything else you can say to help me understand?
How would moving the devicetree files to another repository (also external to
the barebox project) resolve the issues for the barebox project?
Thanks,
-Frank
--
To unsubscribe from this list: send the line "unsubscribe devicetree-spec" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/3] NFC: trf7970a: Add driver with ISO/IEC 14443 Type 2 Tag Support
From: Felipe Balbi @ 2014-02-21 1:20 UTC (permalink / raw)
To: Samuel Ortiz
Cc: Mark A. Greer, Lauro Ramos Venancio, Aloisio Almeida Jr,
Felipe Balbi, Erick Macias, Thierry Escande,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-nfc-y27Ovi1pjclAfugRpC6u6w,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20140221005059.GS18868@zurbaran>
[-- Attachment #1: Type: text/plain, Size: 233 bytes --]
Hi,
On Fri, Feb 21, 2014 at 01:50:59AM +0100, Samuel Ortiz wrote:
> > +#define TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA BIT(0)
> Are there actually any TRF970 devices that do not need this quirk ?
in the oven ;-)
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v4 0/4] Bugfix for of_match_node ordering
From: Sören Brinkmann @ 2014-02-21 1:22 UTC (permalink / raw)
To: Grant Likely
Cc: Sachin Kamat, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
LKML, Kevin Hao, Rob Herring, Sebastian Hesselbarth,
linux-samsung-soc
In-Reply-To: <CACxGe6sg7FpJqAUjkU5DkUpiV_mDbtSV3RETtiQdACUXhRtRrg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Grant,
On Thu, 2014-02-20 at 11:29AM +0000, Grant Likely wrote:
> On Thu, Feb 20, 2014 at 8:39 AM, Sachin Kamat <sachin.kamat-QSEj5FYQhm5QFI55V6+gNQ@public.gmane.orgg> wrote:
> > Hi Grant,
> >
> > I observe the following boot failure with today's (next-20140220) linux-next
> > tree on Exynos based boards with the default exynos_defconfig.
>
> Ugh, nested locking. that is not good. Kevin's patch looks correct and
> I'll merge it in. I'm a little disturbed though that you're the only
> one who has reported problems. Looking at what it does I would expect
> pretty much every SMP platform for freak out about this, but I've
> heard nothing from the powerpc guys.
>
> I'll merge in the fix of course, but I'd like to know what I'm missing.
FWIW, I see a deadlock on Zynq (ARM) as well. The proposed patch
resolves it. Feel free to add my
Tested-by: Soren Brinkmann <soren.brinkmann-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
Sören
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5] bus: imx-weim: support CS GPR configuration
From: Shawn Guo @ 2014-02-21 1:36 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Philippe De Muyter, Alexander Shiyan, Huang Shijie,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Rob Herring, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1392705666-10995-1-git-send-email-shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
On Tue, Feb 18, 2014 at 02:41:06PM +0800, Shawn Guo wrote:
> For imx50-weim and imx6q-weim type of devices, there might a WEIM CS
> space configuration register in General Purpose Register controller,
> e.g. IOMUXC_GPR1 on i.MX6Q.
>
> Depending on which configuration of the following 4 is chosen for given
> system, IOMUXC_GPR1[11:0] should be set up as 05, 033, 0113 or 01111
> correspondingly.
>
> CS0(128M) CS1(0M) CS2(0M) CS3(0M)
> CS0(64M) CS1(64M) CS2(0M) CS3(0M)
> CS0(64M) CS1(32M) CS2(32M) CS3(0M)
> CS0(32M) CS1(32M) CS2(32M) CS3(32M)
>
> The patch creates a function for such type of devices, which scans
> 'ranges' property of WEIM node and build the GPR value incrementally.
> Thus the WEIM CS GPR can be set up automatically at boot time.
>
> Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> Changes since v4:
> - Fix a typo in comment
> - Add fsl,weim-cs-gpr in the binding example
> - Check return of imx_weim_gpr_setup()
>
> Documentation/devicetree/bindings/bus/imx-weim.txt | 28 +++++++++-
Rob, Mark,
Any objections on this binding update? Otherwise, I will queue this
patch for 3.15.
Shawn
> drivers/bus/imx-weim.c | 58 ++++++++++++++++++++
> 2 files changed, 85 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt
> index 0fd76c4..6630d84 100644
> --- a/Documentation/devicetree/bindings/bus/imx-weim.txt
> +++ b/Documentation/devicetree/bindings/bus/imx-weim.txt
> @@ -8,7 +8,12 @@ The actual devices are instantiated from the child nodes of a WEIM node.
>
> Required properties:
>
> - - compatible: Should be set to "fsl,<soc>-weim"
> + - compatible: Should contain one of the following:
> + "fsl,imx1-weim"
> + "fsl,imx27-weim"
> + "fsl,imx51-weim"
> + "fsl,imx50-weim"
> + "fsl,imx6q-weim"
> - reg: A resource specifier for the register space
> (see the example below)
> - clocks: the clock, see the example below.
> @@ -19,6 +24,26 @@ Required properties:
>
> <cs-number> 0 <physical address of mapping> <size>
>
> +Optional properties:
> +
> + - fsl,weim-cs-gpr: For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
> + devices, it should be the phandle to the system General
> + Purpose Register controller that contains WEIM CS GPR
> + register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0]
> + should be set up as one of the following 4 possible
> + values depending on the CS space configuration.
> +
> + IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3
> + ---------------------------------------------
> + 05 128M 0M 0M 0M
> + 033 64M 64M 0M 0M
> + 0113 64M 32M 32M 0M
> + 01111 32M 32M 32M 32M
> +
> + In case that the property is absent, the reset value or
> + what bootloader sets up in IOMUXC_GPR1[11:0] will be
> + used.
> +
> Timing property for child nodes. It is mandatory, not optional.
>
> - fsl,weim-cs-timing: The timing array, contains timing values for the
> @@ -43,6 +68,7 @@ Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM:
> #address-cells = <2>;
> #size-cells = <1>;
> ranges = <0 0 0x08000000 0x08000000>;
> + fsl,weim-cs-gpr = <&gpr>;
>
> nor@0,0 {
> compatible = "cfi-flash";
> diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
> index 3ef58c8..f8ee13c 100644
> --- a/drivers/bus/imx-weim.c
> +++ b/drivers/bus/imx-weim.c
> @@ -11,6 +11,9 @@
> #include <linux/clk.h>
> #include <linux/io.h>
> #include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
> +#include <linux/regmap.h>
>
> struct imx_weim_devtype {
> unsigned int cs_count;
> @@ -56,6 +59,55 @@ static const struct of_device_id weim_id_table[] = {
> };
> MODULE_DEVICE_TABLE(of, weim_id_table);
>
> +static int __init imx_weim_gpr_setup(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct property *prop;
> + const __be32 *p;
> + struct regmap *gpr;
> + u32 gprvals[4] = {
> + 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
> + 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */
> + 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */
> + 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */
> + };
> + u32 gprval = 0;
> + u32 val;
> + int cs = 0;
> + int i = 0;
> +
> + gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
> + if (IS_ERR(gpr)) {
> + dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
> + return 0;
> + }
> +
> + of_property_for_each_u32(np, "ranges", prop, p, val) {
> + if (i % 4 == 0) {
> + cs = val;
> + } else if (i % 4 == 3 && val) {
> + val = (val / SZ_32M) | 1;
> + gprval |= val << cs * 3;
> + }
> + i++;
> + }
> +
> + if (i == 0 || i % 4)
> + goto err;
> +
> + for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
> + if (gprval == gprvals[i]) {
> + /* Found it. Set up IOMUXC_GPR1[11:0] with it. */
> + regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
> + return 0;
> + }
> + }
> +
> +err:
> + dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
> + return -EINVAL;
> +}
> +
> /* Parse and set the timing for this device. */
> static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
> const struct imx_weim_devtype *devtype)
> @@ -92,6 +144,12 @@ static int __init weim_parse_dt(struct platform_device *pdev,
> struct device_node *child;
> int ret;
>
> + if (devtype == &imx50_weim_devtype) {
> + ret = imx_weim_gpr_setup(pdev);
> + if (ret)
> + return ret;
> + }
> +
> for_each_child_of_node(pdev->dev.of_node, child) {
> if (!child->name)
> continue;
> --
> 1.7.9.5
>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 1/4] ARM: dts: imx6q-phytec: Added USB_OTG Support
From: Shawn Guo @ 2014-02-21 2:14 UTC (permalink / raw)
To: Ashutosh singh
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
c.hemp-guT5V/WYfQezQB+pC5nmwQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, festevam-Re5JQEeQqe8AvxtiuMwx3w
In-Reply-To: <1392875418-9034-1-git-send-email-ashutosh.s-mS2nBM426Az/PtFMR13I2A@public.gmane.org>
On Thu, Feb 20, 2014 at 11:20:15AM +0530, Ashutosh singh wrote:
> This patch adds support for USB_OTG on Phytec phyFLEX-i.MX6 Quad module.
>
> Signed-off-by: Ashutosh singh <ashutosh.s-mS2nBM426Az/PtFMR13I2A@public.gmane.org>
Applied all 4, thanks.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox