* [PATCH v6 10/11] thermal: armada: Wait sensors validity before exiting the init callback
From: Miquel Raynal @ 2017-12-22 9:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222093226.23456-1-miquel.raynal@free-electrons.com>
The thermal core will check for sensors validity right after the
initialization callback has returned. As the initialization routine make
a reset, the sensors are not ready immediately and the core spawns an
error in the dmesg. Avoid this annoying situation by polling on the
validity bit before exiting from these routines. This also avoid the use
of blind sleeps.
Suggested-by: David Sniatkiwicz <davidsn@marvell.com>
Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/thermal/armada_thermal.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
index a3feaef7db20..ea958e651312 100644
--- a/drivers/thermal/armada_thermal.c
+++ b/drivers/thermal/armada_thermal.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/thermal.h>
+#include <linux/iopoll.h>
/* Thermal Manager Control and Status Register */
#define PMU_TDC0_SW_RST_MASK (0x1 << 1)
@@ -59,6 +60,9 @@
#define CONTROL1_EXT_TSEN_SW_RESET BIT(7)
#define CONTROL1_EXT_TSEN_HW_RESETn BIT(8)
+#define STATUS_POLL_PERIOD_US 1000
+#define STATUS_POLL_TIMEOUT_US 100000
+
struct armada_thermal_data;
/* Marvell EBU Thermal Sensor Dev Structure */
@@ -155,6 +159,16 @@ static void armada375_init_sensor(struct platform_device *pdev,
msleep(50);
}
+static void armada_wait_sensor_validity(struct armada_thermal_priv *priv)
+{
+ u32 reg;
+
+ readl_relaxed_poll_timeout(priv->status, reg,
+ reg & priv->data->is_valid_bit,
+ STATUS_POLL_PERIOD_US,
+ STATUS_POLL_TIMEOUT_US);
+}
+
static void armada380_init_sensor(struct platform_device *pdev,
struct armada_thermal_priv *priv)
{
@@ -164,7 +178,6 @@ static void armada380_init_sensor(struct platform_device *pdev,
reg |= CONTROL1_EXT_TSEN_HW_RESETn;
reg &= ~CONTROL1_EXT_TSEN_SW_RESET;
writel(reg, priv->control1);
- msleep(10);
/* Set Tsen Tc Trim to correct default value (errata #132698) */
if (priv->control0) {
@@ -172,8 +185,10 @@ static void armada380_init_sensor(struct platform_device *pdev,
reg &= ~CONTROL0_TSEN_TC_TRIM_MASK;
reg |= CONTROL0_TSEN_TC_TRIM_VAL;
writel(reg, priv->control0);
- msleep(10);
}
+
+ /* Wait the sensors to be valid or the core will warn the user */
+ armada_wait_sensor_validity(priv);
}
static void armada_ap806_init_sensor(struct platform_device *pdev,
@@ -185,7 +200,9 @@ static void armada_ap806_init_sensor(struct platform_device *pdev,
reg &= ~CONTROL0_TSEN_RESET;
reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_ENABLE;
writel(reg, priv->control0);
- msleep(10);
+
+ /* Wait the sensors to be valid or the core will warn the user */
+ armada_wait_sensor_validity(priv);
}
static bool armada_is_valid(struct armada_thermal_priv *priv)
--
2.11.0
^ permalink raw reply related
* [PATCH v6 11/11] thermal: armada: Give meaningful names to the thermal zones
From: Miquel Raynal @ 2017-12-22 9:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222093226.23456-1-miquel.raynal@free-electrons.com>
After registration to the thermal core, sysfs will make one entry
per instance of the driver in /sys/class/thermal_zoneX and
/sys/class/hwmon/hwmonX, X being the index of the instance, all of them
having the type/name "armada_thermal".
Until now there was only one thermal zone per SoC but SoCs like Armada
A7K and Armada A8K have respectively two and three thermal zones (one
per AP and one per CP) and this number is subject to grow in the future.
Use dev_name() instead of the "armada_thermal" string to get a
meaningful name and be able to identify the thermal zones from
userspace.
Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/thermal/armada_thermal.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
index ea958e651312..454137f78eb3 100644
--- a/drivers/thermal/armada_thermal.c
+++ b/drivers/thermal/armada_thermal.c
@@ -406,8 +406,8 @@ static int armada_thermal_probe(struct platform_device *pdev)
priv->data->init_sensor(pdev, priv);
- thermal = thermal_zone_device_register("armada_thermal", 0, 0,
- priv, &ops, NULL, 0, 0);
+ thermal = thermal_zone_device_register(dev_name(&pdev->dev), 0, 0, priv,
+ &ops, NULL, 0, 0);
if (IS_ERR(thermal)) {
dev_err(&pdev->dev,
"Failed to register thermal zone device\n");
--
2.11.0
^ permalink raw reply related
* [PATCH] arm64: dts: hi3798cv200: add SD card support
From: Wei Xu @ 2017-12-22 9:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1508036687-13926-1-git-send-email-shawnguo@kernel.org>
Hi Shawn,
On 2017/10/15 4:04, Shawn Guo wrote:
> From: Shawn Guo <shawn.guo@linaro.org>
>
> It adds device mmc at 9820000 which is used as SD card on poplar board.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Applied into hisilicon dt tree.
Thanks!
Best Regards,
Wei
> ---
> arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts | 6 ++++++
> arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi | 12 ++++++++++++
> 2 files changed, 18 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts
> index b9142871d6fe..e6fd9f296497 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts
> +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200-poplar.dts
> @@ -146,6 +146,12 @@
> status = "okay";
> };
>
> +&sd0 {
> + bus-width = <4>;
> + cap-sd-highspeed;
> + status = "okay";
> +};
> +
> &spi0 {
> status = "okay";
> label = "LS-SPI0";
> diff --git a/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi b/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi
> index 75865f8a862a..962bd79139e4 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi
> +++ b/arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi
> @@ -192,6 +192,18 @@
> status = "disabled";
> };
>
> + sd0: mmc at 9820000 {
> + compatible = "snps,dw-mshc";
> + reg = <0x9820000 0x10000>;
> + interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&crg HISTB_SDIO0_CIU_CLK>,
> + <&crg HISTB_SDIO0_BIU_CLK>;
> + clock-names = "ciu", "biu";
> + resets = <&crg 0x9c 4>;
> + reset-names = "reset";
> + status = "disabled";
> + };
> +
> emmc: mmc at 9830000 {
> compatible = "snps,dw-mshc";
> reg = <0x9830000 0x10000>;
>
^ permalink raw reply
* [PATCH RFC 1/4] crypto: engine - Permit to enqueue all async requests
From: Corentin Labbe @ 2017-12-22 9:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222090603.GB32542@gondor.apana.org.au>
On Fri, Dec 22, 2017 at 08:06:03PM +1100, Herbert Xu wrote:
> On Fri, Dec 22, 2017 at 09:41:48AM +0100, Corentin Labbe wrote:
> >
> > It's you that was suggesting using crypto_async_request:
> > https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1474434.html
> > "The only wart with this scheme is that the drivers end up seeing
> > struct crypto_async_request and will need to convert that to the
> > respective request types but I couldn't really find a better way."
> >
> > So I wait for any suggestion.
>
> The core engine code obviously will use the base type but it should
> not be exposed to the driver authors. IOW all exposed API should
> take the final types such as aead_request before casting it.
>
For driver->engine calls(crypto_finalize_request/crypto_transfer_request_to_engine) it's easy.
But I do not see how to do it for crypto_engine_op appart re-introducing the big if/then/else that
you didnt want.
Or do you agree to set the request parameter for crypto_engine_op(prepare_request/unprepare_request/do_one_request) to void * ?
Regards
^ permalink raw reply
* [PATCH v2] arm64: dts: hi3660: improve pmu description
From: Wei Xu @ 2017-12-22 9:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1510226303-20950-1-git-send-email-xuyiping@hisilicon.com>
Hi Xu YiPing,
On 2017/11/9 11:18, Xu YiPing wrote:
> cortex-a73 pmu driver is supported now. hi3660 is 4*a73 + 4*a53, so it
> should use "cortex-a73-pmu" and "cortex-a53-pmu" instead of "armpmu-v3",
> then we can use the a73 and a53 events in perf tool directly.
>
> Signed-off-by: Xu YiPing <xuyiping@hisilicon.com>
Applied into hisilicon dt tree.
Thanks!
Best Regards,
Wei
> ---
> arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 22 +++++++++++++---------
> 1 file changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> index 13ae69f..f4882d3 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> +++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> @@ -203,21 +203,25 @@
> IRQ_TYPE_LEVEL_HIGH)>;
> };
>
> - pmu {
> - compatible = "arm,armv8-pmuv3";
> + a53-pmu {
> + compatible = "arm,cortex-a53-pmu";
> interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>,
> <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>,
> <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>,
> - <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>,
> - <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
> - <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
> - <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
> - <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
> + <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
> interrupt-affinity = <&cpu0>,
> <&cpu1>,
> <&cpu2>,
> - <&cpu3>,
> - <&cpu4>,
> + <&cpu3>;
> + };
> +
> + a73-pmu {
> + compatible = "arm,cortex-a73-pmu";
> + interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-affinity = <&cpu4>,
> <&cpu5>,
> <&cpu6>,
> <&cpu7>;
>
^ permalink raw reply
* [PATCH v2] arm64: dts: Hi3660: Fix up psci state id
From: Wei Xu @ 2017-12-22 9:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1513069954-22827-1-git-send-email-leo.yan@linaro.org>
Hi Leo,
On 2017/12/12 9:12, Leo Yan wrote:
> Thanks a lot for Vincent Guittot careful work to find bug for 'CPU_NAP'
> idle state. From ftrace log we can observe CA73 CPUs can be easily
> waken up from 'CPU_NAP' state but the 'waken up' CPUs doesn't handle
> anything and sleep again; so there have tons of trace events for CA73
> CPUs entering and exiting idle state.
>
> On Hi3660 CA73 has retention state 'CPU_NAP' for CPU idle, this state we
> set its psci parameter as '0x0000001' and from this parameter it can
> calculate state id is 1. Unfortunately ARM trusted firmware (ARM-TF)
> takes 1 as a invalid value for state id, so the CPU cannot enter idle
> state and directly bail out to kernel.
>
> We want to create good practice for psci parameters platform definition,
> so review the psci specification. The spec "ARM Power State Coordination
> Interface - Platform Design Document (ARM DEN 0022D)" recommends state
> ID in chapter "6.5 Recommended StateID Encoding". The recommended power
> state IDs can be presented by below listed values; and it divides into
> three fields, every field can use 4 bits to present power states
> corresponding to core level, cluster level and system level:
> 0: Run
> 1: Standby
> 2: Retention
> 3: Powerdown
>
> This commit changes psci parameter to compliance with the suggested
> state ID in the doc. Except we change 'CPU_NAP' state psci parameter
> to '0x0000002', this commit also changes 'CPU_SLEEP' and 'CLUSTER_SLEEP'
> state parameters to '0x0010003' and '0x1010033' respectively.
>
> Credits to Daniel, Sudeep and Soby for suggestion and consolidation.
>
> Cc: Vincent Guittot <vincent.guittot@linaro.org>
> Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
> Cc: Sudeep Holla <sudeep.holla@arm.com>
> Cc: Soby Mathew <Soby.Mathew@arm.com>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> ---
Applied into hisilicon dt tree.
Thanks!
Best Regards,
Wei
> arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> index ab0b95b..99d5a46 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> +++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> @@ -147,7 +147,7 @@
>
> CPU_NAP: cpu-nap {
> compatible = "arm,idle-state";
> - arm,psci-suspend-param = <0x0000001>;
> + arm,psci-suspend-param = <0x0000002>;
> entry-latency-us = <7>;
> exit-latency-us = <2>;
> min-residency-us = <15>;
> @@ -156,7 +156,7 @@
> CPU_SLEEP: cpu-sleep {
> compatible = "arm,idle-state";
> local-timer-stop;
> - arm,psci-suspend-param = <0x0010000>;
> + arm,psci-suspend-param = <0x0010003>;
> entry-latency-us = <40>;
> exit-latency-us = <70>;
> min-residency-us = <3000>;
> @@ -165,7 +165,7 @@
> CLUSTER_SLEEP_0: cluster-sleep-0 {
> compatible = "arm,idle-state";
> local-timer-stop;
> - arm,psci-suspend-param = <0x1010000>;
> + arm,psci-suspend-param = <0x1010033>;
> entry-latency-us = <500>;
> exit-latency-us = <5000>;
> min-residency-us = <20000>;
> @@ -174,7 +174,7 @@
> CLUSTER_SLEEP_1: cluster-sleep-1 {
> compatible = "arm,idle-state";
> local-timer-stop;
> - arm,psci-suspend-param = <0x1010000>;
> + arm,psci-suspend-param = <0x1010033>;
> entry-latency-us = <1000>;
> exit-latency-us = <5000>;
> min-residency-us = <20000>;
>
^ permalink raw reply
* [PATCH] arm64: dts: hisilicon: Add hi3660 cpu capacity-dmips-mhz information
From: Wei Xu @ 2017-12-22 9:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1513174866-6678-1-git-send-email-valentin.schneider@arm.com>
Hi Valentin,
On 2017/12/13 14:21, Valentin Schneider wrote:
> The following dt entries are added:
> cpus [0-3] (Cortex A53):
> - capacity-dmips-mhz = <592>;
>
> cpus [4-7] (Cortex A73):
> - capacity-dmips-mhz = <1024>;
>
> Those values were obtained by running dhrystone 2.1 on a
> HiKey960 with the following procedure:
> - Offline all CPUs but CPU0 (A53)
> - Set CPU0 frequency to maximum
> - Run Dhrystone 2.1 for 20 seconds
>
> - Offline all CPUs but CPU4 (A73)
> - set CPU4 frequency to maximum
> - Run Dhrystone 2.1 for 20 seconds
>
> The results are as follows:
> A53: 129633887 loops
> A73: 287034147 loops
>
> By scaling those values so that the A73s use 1024, we end up with 462
> for the A53s. However, they have different maximum frequencies:
> 1.844GHz for A53s and 2.362GHz for A73s. Thus, we can scale the A53
> value to truly represent dmips per MHz, and we end up with 592.
>
> The impact of this change can be verified on HiKey960:
>
> $ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
> 1844000
> 1844000
> 1844000
> 1844000
> 2362000
> 2362000
> 2362000
> 2362000
>
> $ cat /sys/devices/system/cpu/cpu*/cpu_capacity
> 462
> 462
> 462
> 462
> 1024
> 1024
> 1024
> 1024
>
> Signed-off-by: Valentin Schneider <valentin.schneider@arm.com>
> ---
Applied into hisilicon dt tree.
Thanks!
Best Regards,
Wei
> arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> index ab0b95b..04a8d28 100644
> --- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> +++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi
> @@ -61,6 +61,7 @@
> enable-method = "psci";
> next-level-cache = <&A53_L2>;
> cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
> + capacity-dmips-mhz = <592>;
> };
>
> cpu1: cpu at 1 {
> @@ -70,6 +71,7 @@
> enable-method = "psci";
> next-level-cache = <&A53_L2>;
> cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
> + capacity-dmips-mhz = <592>;
> };
>
> cpu2: cpu at 2 {
> @@ -79,6 +81,7 @@
> enable-method = "psci";
> next-level-cache = <&A53_L2>;
> cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
> + capacity-dmips-mhz = <592>;
> };
>
> cpu3: cpu at 3 {
> @@ -88,6 +91,7 @@
> enable-method = "psci";
> next-level-cache = <&A53_L2>;
> cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP_0>;
> + capacity-dmips-mhz = <592>;
> };
>
> cpu4: cpu at 100 {
> @@ -101,6 +105,7 @@
> &CPU_SLEEP
> &CLUSTER_SLEEP_1
> >;
> + capacity-dmips-mhz = <1024>;
> };
>
> cpu5: cpu at 101 {
> @@ -114,6 +119,7 @@
> &CPU_SLEEP
> &CLUSTER_SLEEP_1
> >;
> + capacity-dmips-mhz = <1024>;
> };
>
> cpu6: cpu at 102 {
> @@ -127,6 +133,7 @@
> &CPU_SLEEP
> &CLUSTER_SLEEP_1
> >;
> + capacity-dmips-mhz = <1024>;
> };
>
> cpu7: cpu at 103 {
> @@ -140,6 +147,7 @@
> &CPU_SLEEP
> &CLUSTER_SLEEP_1
> >;
> + capacity-dmips-mhz = <1024>;
> };
>
> idle-states {
> --
> 2.7.4
>
>
> .
>
^ permalink raw reply
* [PATCH] ARM: OMAP2+: hwmod_core: enable optional clocks before main clock
From: Keerthy @ 2017-12-22 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1513934763-23966-1-git-send-email-t-kristo@ti.com>
On Friday 22 December 2017 02:56 PM, Tero Kristo wrote:
> The optional clocks must be enabled before the main clock after the
> transition to clkctrl controlled clocks is done. Otherwise the module
> we attempt to enable might be stuck in transition.
>
> Reported-by: Keerthy <j-keerthy@ti.com>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
Tested the patch on both DRA7-EVM and DRA72-EVM. I no longer see the issue:
Tested-by: Keerthy <j-keerthy@ti.com>
> ---
> Hi Tony,
>
> This patch fixes a regression seen in linux-next, where certain peripherals
> fail to enable after the clkctrl changes are in. The case seen has been
> with mcasp3, where it fails to transition to enabled during the audio
> driver probe. Not sure where you want to pick this up, maybe as early
> rc fixes if its too late to push this to linux-next?
>
> arch/arm/mach-omap2/omap_hwmod.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
> index 7324048..340d05c 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -976,6 +976,9 @@ static int _enable_clocks(struct omap_hwmod *oh)
>
> pr_debug("omap_hwmod: %s: enabling clocks\n", oh->name);
>
> + if (oh->flags & HWMOD_OPT_CLKS_NEEDED)
> + _enable_optional_clocks(oh);
> +
> if (oh->_clk)
> clk_enable(oh->_clk);
>
> @@ -984,9 +987,6 @@ static int _enable_clocks(struct omap_hwmod *oh)
> clk_enable(os->_clk);
> }
>
> - if (oh->flags & HWMOD_OPT_CLKS_NEEDED)
> - _enable_optional_clocks(oh);
> -
> /* The opt clocks are controlled by the device driver. */
>
> return 0;
>
^ permalink raw reply
* [PATCH v4 1/2] dt-bindings: media: Add Allwinner V3s Camera Sensor Interface (CSI)
From: Yong Deng @ 2017-12-22 9:41 UTC (permalink / raw)
To: linux-arm-kernel
Add binding documentation for Allwinner V3s CSI.
Signed-off-by: Yong Deng <yong.deng@magewell.com>
---
.../devicetree/bindings/media/sun6i-csi.txt | 51 ++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/sun6i-csi.txt
diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt
new file mode 100644
index 0000000..b5bfe3f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt
@@ -0,0 +1,51 @@
+Allwinner V3s Camera Sensor Interface
+------------------------------
+
+Required properties:
+ - compatible: value must be "allwinner,sun8i-v3s-csi"
+ - reg: base address and size of the memory-mapped region.
+ - interrupts: interrupt associated to this IP
+ - clocks: phandles to the clocks feeding the CSI
+ * bus: the CSI interface clock
+ * mod: the CSI module clock
+ * ram: the CSI DRAM clock
+ - clock-names: the clock names mentioned above
+ - resets: phandles to the reset line driving the CSI
+
+- ports: A ports node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt.
+ Currently, the driver only support the parallel interface. So, a single port
+ node with one endpoint and parallel bus is supported.
+
+Example:
+
+ csi1: csi at 1cb4000 {
+ compatible = "allwinner,sun8i-v3s-csi";
+ reg = <0x01cb4000 0x1000>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_CSI>,
+ <&ccu CLK_CSI1_SCLK>,
+ <&ccu CLK_DRAM_CSI>;
+ clock-names = "bus", "mod", "ram";
+ resets = <&ccu RST_BUS_CSI>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* Parallel bus endpoint */
+ csi1_ep: endpoint {
+ remote-endpoint = <&adv7611_ep>;
+ bus-width = <16>;
+ data-shift = <0>;
+
+ /* If hsync-active/vsync-active are missing,
+ embedded BT.656 sync is used */
+ hsync-active = <0>; /* Active low */
+ vsync-active = <0>; /* Active low */
+ data-active = <1>; /* Active high */
+ pclk-sample = <1>; /* Rising */
+ };
+ };
+ };
+
--
1.8.3.1
^ permalink raw reply related
* [PATCH v6 01/11] dt-bindings: thermal: Describe Armada AP806 and CP110
From: Miquel RAYNAL @ 2017-12-22 9:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222093226.23456-2-miquel.raynal@free-electrons.com>
Hi Rob,
On Fri, 22 Dec 2017 10:32:16 +0100
Miquel Raynal <miquel.raynal@free-electrons.com> wrote:
> From: Baruch Siach <baruch@tkos.co.il>
>
> Add compatible strings for AP806 and CP110 that are part of the Armada
> 8k/7k line of SoCs.
>
> Add a note on the differences in the size of the control area in
> different bindings. This is an existing difference between the Armada
> 375 binding and the other boards already supported. The new AP806 and
> CP110 bindings are similar to the existing Armada 375 in this regard.
>
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>
> [<miquel.raynal@free-electrons.com>: reword, additional details]
> Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
I forgot to add your:
Reviewed-by: Rob Herring <robh@kernel.org>
in this series, sorry about that.
Kind regards,
Miqu?l
> ---
> .../devicetree/bindings/thermal/armada-thermal.txt | 37
> +++++++++++++++------- 1 file changed, 25 insertions(+), 12
> deletions(-)
>
> diff --git
> a/Documentation/devicetree/bindings/thermal/armada-thermal.txt
> b/Documentation/devicetree/bindings/thermal/armada-thermal.txt index
> 24aacf8948c5..e0d013a2e66d 100644 ---
> a/Documentation/devicetree/bindings/thermal/armada-thermal.txt +++
> b/Documentation/devicetree/bindings/thermal/armada-thermal.txt @@
> -2,22 +2,35 @@ Required properties:
>
> -- compatible: Should be set to one of the following:
> - marvell,armada370-thermal
> - marvell,armada375-thermal
> - marvell,armada380-thermal
> - marvell,armadaxp-thermal
> +- compatible: Should be set to one of the following:
> + * marvell,armada370-thermal
> + * marvell,armada375-thermal
> + * marvell,armada380-thermal
> + * marvell,armadaxp-thermal
> + * marvell,armada-ap806-thermal
> + * marvell,armada-cp110-thermal
>
> -- reg: Device's register space.
> - Two entries are expected, see the examples below.
> - The first one is required for the sensor register;
> - the second one is required for the control register
> - to be used for sensor initialization (a.k.a.
> calibration). +- reg: Device's register space.
> + Two entries are expected, see the examples below. The first one
> points
> + to the status register (4B). The second one points to the control
> + registers (8B).
> + Note: The compatibles marvell,armada370-thermal,
> + marvell,armada380-thermal, and marvell,armadaxp-thermal must point
> to
> + "control MSB/control 1", with size of 4 (deprecated binding), or
> point
> + to "control LSB/control 0" with size of 8 (current binding). All
> other
> + compatibles must point to "control LSB/control 0" with size of 8.
>
> -Example:
> +Examples:
>
> + /* Legacy bindings */
> thermal at d0018300 {
> compatible = "marvell,armada370-thermal";
> - reg = <0xd0018300 0x4
> + reg = <0xd0018300 0x4
> 0xd0018304 0x4>;
> };
> +
> + ap_thermal: thermal at 6f8084 {
> + compatible = "marvell,armada-ap806-thermal";
> + reg = <0x6f808C 0x4>,
> + <0x6f8084 0x8>;
> + };
--
Miquel Raynal, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [GIT PULL] arm64: dts: hisilicon dts updates for v4.16
From: Wei Xu @ 2017-12-22 9:44 UTC (permalink / raw)
To: linux-arm-kernel
Hi Arnd, Hi Olof, Hi Kevin,
Please help to pull the following changes.
Thanks!
Best Regards,
Wei
---
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
are available in the git repository at:
git://github.com/hisilicon/linux-hisi.git tags/hisi-arm64-dt-for-4.16
for you to fetch changes up to 2b49083a2d93b1adb755f057ed569d1ae6a934c7:
arm64: dts: Hi3660: Fix up psci state id (2017-12-22 09:11:43 +0000)
----------------------------------------------------------------
ARM64: DT: Hisilicon SoC DT updates for 4.15
- Add SD card support for hi3798cv200-poplar board
- Replace the PMU node with exact match on hi3660 SoC
- Add cpu capacity-dmips-mhz information on hi3660 SoC
- Fix up the PSCI state ID on hi3660 SoC
----------------------------------------------------------------
Leo Yan (1):
arm64: dts: Hi3660: Fix up psci state id
Shawn Guo (1):
arm64: dts: hi3798cv200: add SD card support
Valentin Schneider (1):
arm64: dts: hisilicon: Add hi3660 cpu capacity-dmips-mhz information
Xu YiPing (1):
arm64: dts: hi3660: improve pmu description
arch/arm64/boot/dts/hisilicon/hi3660.dtsi | 38 ++++++++++++++--------
.../boot/dts/hisilicon/hi3798cv200-poplar.dts | 6 ++++
arch/arm64/boot/dts/hisilicon/hi3798cv200.dtsi | 12 +++++++
3 files changed, 43 insertions(+), 13 deletions(-)
^ permalink raw reply
* [RFC PATCH V1 1/2] clk: use atomic runtime pm api in clk_core_is_enabled
From: Dong Aisheng @ 2017-12-22 9:46 UTC (permalink / raw)
To: linux-arm-kernel
Current clk_pm_runtime_put is using pm_runtime_put_sync which
is not safe to be called in clk_core_is_enabled as it should
be able to run in atomic context.
Thus use pm_runtime_put instead which is atomic safe.
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Fixes: 9a34b45397e5 ("clk: Add support for runtime PM")
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
drivers/clk/clk.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 5ec5809..e24968f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -227,7 +227,8 @@ static bool clk_core_is_enabled(struct clk_core *core)
ret = core->ops->is_enabled(core->hw);
done:
- clk_pm_runtime_put(core);
+ if (core->dev)
+ pm_runtime_put(core->dev);
return ret;
}
--
2.7.4
^ permalink raw reply related
* [RFC PATCH V1 2/2] clk: add lock for clk_core_is_enabled
From: Dong Aisheng @ 2017-12-22 9:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1513935965-12909-1-git-send-email-aisheng.dong@nxp.com>
According to design doc, .is_enabled should be protected by enable lock.
Then users don't have to protect it against enable/disable operation
in clock drivers.
See: Documentation/clk.txt
"The enable lock is a spinlock and is held across calls to the .enable,
.disable and .is_enabled operations."
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
drivers/clk/clk.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index e24968f..d6e2d5c 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -198,14 +198,19 @@ static bool clk_core_is_prepared(struct clk_core *core)
static bool clk_core_is_enabled(struct clk_core *core)
{
+ unsigned long flags;
bool ret = false;
+ flags = clk_enable_lock();
+
/*
* .is_enabled is only mandatory for clocks that gate
* fall back to software usage counter if .is_enabled is missing
*/
- if (!core->ops->is_enabled)
+ if (!core->ops->is_enabled) {
+ clk_enable_unlock(flags);
return core->enable_count;
+ }
/*
* Check if clock controller's device is runtime active before
@@ -230,6 +235,8 @@ static bool clk_core_is_enabled(struct clk_core *core)
if (core->dev)
pm_runtime_put(core->dev);
+ clk_enable_unlock(flags);
+
return ret;
}
--
2.7.4
^ permalink raw reply related
* [PATCH v4 2/2] media: V3s: Add support for Allwinner CSI.
From: Yong Deng @ 2017-12-22 9:47 UTC (permalink / raw)
To: linux-arm-kernel
Allwinner V3s SoC have two CSI module. CSI0 is used for MIPI interface
and CSI1 is used for parallel interface. This is not documented in
datasheet but by testing and guess.
This patch implement a v4l2 framework driver for it.
Currently, the driver only support the parallel interface. MIPI-CSI2,
ISP's support are not included in this patch.
Signed-off-by: Yong Deng <yong.deng@magewell.com>
---
MAINTAINERS | 8 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/sunxi/sun6i-csi/Kconfig | 9 +
drivers/media/platform/sunxi/sun6i-csi/Makefile | 3 +
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 878 +++++++++++++++++++++
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 147 ++++
.../media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 203 +++++
.../media/platform/sunxi/sun6i-csi/sun6i_video.c | 752 ++++++++++++++++++
.../media/platform/sunxi/sun6i-csi/sun6i_video.h | 60 ++
10 files changed, 2063 insertions(+)
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/Kconfig
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/Makefile
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 9501403..b792fe5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3783,6 +3783,14 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained
F: sound/pci/cs5535audio/
+CSI DRIVERS FOR ALLWINNER V3s
+M: Yong Deng <yong.deng@magewell.com>
+L: linux-media at vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/platform/sunxi/sun6i-csi/
+F: Documentation/devicetree/bindings/media/sun6i-csi.txt
+
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index fd0c998..41017e3 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -150,6 +150,7 @@ source "drivers/media/platform/am437x/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"
source "drivers/media/platform/rcar-vin/Kconfig"
source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
config VIDEO_TI_CAL
tristate "TI CAL (Camera Adaptation Layer) driver"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 003b0bb..e6e9ce7 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -97,3 +97,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/
obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
obj-y += meson/
+
+obj-$(CONFIG_VIDEO_SUN6I_CSI) += sunxi/sun6i-csi/
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Kconfig b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
new file mode 100644
index 0000000..314188a
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_SUN6I_CSI
+ tristate "Allwinner V3s Camera Sensor Interface driver"
+ depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select REGMAP_MMIO
+ select V4L2_FWNODE
+ ---help---
+ Support for the Allwinner Camera Sensor Interface Controller on V3s.
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile
new file mode 100644
index 0000000..213cb6b
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile
@@ -0,0 +1,3 @@
+sun6i-csi-y += sun6i_video.o sun6i_csi.o
+
+obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
new file mode 100644
index 0000000..8f3f2d6
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -0,0 +1,878 @@
+/*
+ * Copyright (c) 2017 Magewell Electronics Co., Ltd. (Nanjing).
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.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.
+ *
+ * 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/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#include "sun6i_csi.h"
+#include "sun6i_csi_reg.h"
+
+#define MODULE_NAME "sun6i-csi"
+
+struct sun6i_csi_dev {
+ struct sun6i_csi csi;
+ struct device *dev;
+
+ struct regmap *regmap;
+ struct clk *clk_mod;
+ struct clk *clk_ram;
+ struct reset_control *rstc_bus;
+
+ int planar_offset[3];
+};
+
+static const u32 supported_pixformats[] = {
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_PIX_FMT_SGBRG8,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_PIX_FMT_SRGGB8,
+ V4L2_PIX_FMT_SBGGR10,
+ V4L2_PIX_FMT_SGBRG10,
+ V4L2_PIX_FMT_SGRBG10,
+ V4L2_PIX_FMT_SRGGB10,
+ V4L2_PIX_FMT_SBGGR12,
+ V4L2_PIX_FMT_SGBRG12,
+ V4L2_PIX_FMT_SGRBG12,
+ V4L2_PIX_FMT_SRGGB12,
+ V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_YVYU,
+ V4L2_PIX_FMT_UYVY,
+ V4L2_PIX_FMT_VYUY,
+ V4L2_PIX_FMT_HM12,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV21,
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_NV16,
+ V4L2_PIX_FMT_NV61,
+ V4L2_PIX_FMT_YUV422P,
+};
+
+static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
+{
+ return container_of(csi, struct sun6i_csi_dev, csi);
+}
+
+int sun6i_csi_get_supported_pixformats(struct sun6i_csi *csi,
+ const u32 **pixformats)
+{
+ if (pixformats != NULL)
+ *pixformats = supported_pixformats;
+
+ return ARRAY_SIZE(supported_pixformats);
+}
+
+/* TODO add 10&12 bit YUV, RGB support */
+bool sun6i_csi_is_format_support(struct sun6i_csi *csi,
+ u32 pixformat, u32 mbus_code)
+{
+ struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+
+ /*
+ * Some video receivers have the ability to be compatible with
+ * 8bit and 16bit bus width.
+ * Identify the media bus format from device tree.
+ */
+ if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
+ || sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
+ && sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
+ switch (pixformat) {
+ case V4L2_PIX_FMT_HM12:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUV422P:
+ switch (mbus_code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ switch (pixformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8);
+ case V4L2_PIX_FMT_SGBRG8:
+ return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8);
+ case V4L2_PIX_FMT_SGRBG8:
+ return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8);
+ case V4L2_PIX_FMT_SRGGB8:
+ return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8);
+ case V4L2_PIX_FMT_SBGGR10:
+ return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10);
+ case V4L2_PIX_FMT_SGBRG10:
+ return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10);
+ case V4L2_PIX_FMT_SGRBG10:
+ return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10);
+ case V4L2_PIX_FMT_SRGGB10:
+ return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10);
+ case V4L2_PIX_FMT_SBGGR12:
+ return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12);
+ case V4L2_PIX_FMT_SGBRG12:
+ return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12);
+ case V4L2_PIX_FMT_SGRBG12:
+ return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12);
+ case V4L2_PIX_FMT_SRGGB12:
+ return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12);
+
+ case V4L2_PIX_FMT_YUYV:
+ return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8);
+ case V4L2_PIX_FMT_YVYU:
+ return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8);
+ case V4L2_PIX_FMT_UYVY:
+ return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8);
+ case V4L2_PIX_FMT_VYUY:
+ return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8);
+
+ case V4L2_PIX_FMT_HM12:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUV422P:
+ switch (mbus_code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
+{
+ struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+ struct regmap *regmap = sdev->regmap;
+ int ret;
+
+ if (!enable) {
+ regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
+
+ clk_disable_unprepare(sdev->clk_ram);
+ clk_disable_unprepare(sdev->clk_mod);
+ reset_control_assert(sdev->rstc_bus);
+ return 0;
+ }
+
+ ret = clk_prepare_enable(sdev->clk_mod);
+ if (ret) {
+ dev_err(sdev->dev, "Enable csi clk err %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sdev->clk_ram);
+ if (ret) {
+ dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(sdev->rstc_bus);
+ if (ret) {
+ dev_err(sdev->dev, "reset err %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
+
+ return 0;
+}
+
+static enum csi_input_fmt get_csi_input_format(u32 mbus_code, u32 pixformat)
+{
+ /* bayer */
+ if ((mbus_code & 0xF000) == 0x3000)
+ return CSI_INPUT_FORMAT_RAW;
+
+ switch (pixformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ return CSI_INPUT_FORMAT_RAW;
+ }
+
+ /* not support YUV420 input format yet */
+ return CSI_INPUT_FORMAT_YUV422;
+}
+
+static enum csi_output_fmt get_csi_output_format(u32 pixformat, u32 field)
+{
+ bool buf_interlaced = false;
+
+ if (field == V4L2_FIELD_INTERLACED
+ || field == V4L2_FIELD_INTERLACED_TB
+ || field == V4L2_FIELD_INTERLACED_BT)
+ buf_interlaced = true;
+
+ switch (pixformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ return buf_interlaced ? CSI_FRAME_RAW_10 : CSI_FIELD_RAW_10;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ return buf_interlaced ? CSI_FRAME_RAW_12 : CSI_FIELD_RAW_12;
+
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
+
+ case V4L2_PIX_FMT_HM12:
+ return buf_interlaced ? CSI_FRAME_MB_YUV420 :
+ CSI_FIELD_MB_YUV420;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ return buf_interlaced ? CSI_FRAME_UV_CB_YUV420 :
+ CSI_FIELD_UV_CB_YUV420;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ return buf_interlaced ? CSI_FRAME_PLANAR_YUV420 :
+ CSI_FIELD_PLANAR_YUV420;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ return buf_interlaced ? CSI_FRAME_UV_CB_YUV422 :
+ CSI_FIELD_UV_CB_YUV422;
+ case V4L2_PIX_FMT_YUV422P:
+ return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 :
+ CSI_FIELD_PLANAR_YUV422;
+ }
+
+ return 0;
+}
+
+static enum csi_input_seq get_csi_input_seq(u32 mbus_code, u32 pixformat)
+{
+
+ switch (pixformat) {
+ case V4L2_PIX_FMT_HM12:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YUV422P:
+ switch (mbus_code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ return CSI_INPUT_SEQ_UYVY;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ return CSI_INPUT_SEQ_VYUY;
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ return CSI_INPUT_SEQ_YUYV;
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ return CSI_INPUT_SEQ_YVYU;
+ }
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_YVU420:
+ switch (mbus_code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ return CSI_INPUT_SEQ_VYUY;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ return CSI_INPUT_SEQ_UYVY;
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ return CSI_INPUT_SEQ_YVYU;
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ return CSI_INPUT_SEQ_YUYV;
+ }
+ break;
+ }
+
+ return CSI_INPUT_SEQ_YUYV;
+}
+
+static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
+{
+ struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep;
+ unsigned char bus_width;
+ u32 flags;
+ u32 cfg;
+
+ bus_width = endpoint->bus.parallel.bus_width;
+
+ regmap_read(sdev->regmap, CSI_IF_CFG_REG, &cfg);
+
+ cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
+ CSI_IF_CFG_IF_DATA_WIDTH_MASK |
+ CSI_IF_CFG_CLK_POL_MASK | CSI_IF_CFG_VREF_POL_MASK |
+ CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK);
+
+ switch (endpoint->bus_type) {
+ case V4L2_MBUS_PARALLEL:
+ cfg |= CSI_IF_CFG_MIPI_IF_CSI;
+
+ flags = endpoint->bus.parallel.flags;
+
+ cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_YUV422_16BIT :
+ CSI_IF_CFG_CSI_IF_YUV422_INTLV;
+
+ if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
+ cfg |= CSI_IF_CFG_FIELD_POSITIVE;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ cfg |= CSI_IF_CFG_VREF_POL_POSITIVE;
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ cfg |= CSI_IF_CFG_HREF_POL_POSITIVE;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
+ break;
+ case V4L2_MBUS_BT656:
+ cfg |= CSI_IF_CFG_MIPI_IF_CSI;
+
+ flags = endpoint->bus.parallel.flags;
+
+ cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_BT1120 :
+ CSI_IF_CFG_CSI_IF_BT656;
+
+ if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
+ cfg |= CSI_IF_CFG_FIELD_POSITIVE;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
+ break;
+ default:
+ dev_warn(sdev->dev, "Unsupported bus type: %d\n",
+ endpoint->bus_type);
+ break;
+ }
+
+ switch (bus_width) {
+ case 8:
+ cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
+ break;
+ case 10:
+ cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
+ break;
+ case 12:
+ cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
+ break;
+ case 16: /* No need to configure DATA_WIDTH for 16bit */
+ break;
+ default:
+ dev_warn(sdev->dev, "Unsupported bus width: %d\n", bus_width);
+ break;
+ }
+
+ regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
+}
+
+static void sun6i_csi_set_format(struct sun6i_csi_dev *sdev)
+{
+ struct sun6i_csi *csi = &sdev->csi;
+ u32 cfg;
+ u32 val;
+
+ regmap_read(sdev->regmap, CSI_CH_CFG_REG, &cfg);
+
+ cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
+ CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
+ CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
+ CSI_CH_CFG_INPUT_SEQ_MASK);
+
+ val = get_csi_input_format(csi->config.code, csi->config.pixelformat);
+ cfg |= CSI_CH_CFG_INPUT_FMT(val);
+
+ val = get_csi_output_format(csi->config.pixelformat, csi->config.field);
+ cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
+
+ val = get_csi_input_seq(csi->config.code, csi->config.pixelformat);
+ cfg |= CSI_CH_CFG_INPUT_SEQ(val);
+
+ if (csi->config.field == V4L2_FIELD_TOP)
+ cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
+ else if (csi->config.field == V4L2_FIELD_BOTTOM)
+ cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
+ else
+ cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
+
+ regmap_write(sdev->regmap, CSI_CH_CFG_REG, cfg);
+}
+
+static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
+{
+ struct sun6i_csi_config *config = &sdev->csi.config;
+ u32 bytesperline_y;
+ u32 bytesperline_c;
+ int *planar_offset = sdev->planar_offset;
+ u32 width = config->width;
+ u32 height = config->height;
+ u32 hor_len = width;
+
+ switch (config->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ hor_len = width * 2;
+ break;
+ }
+
+ regmap_write(sdev->regmap, CSI_CH_HSIZE_REG,
+ CSI_CH_HSIZE_HOR_LEN(hor_len) |
+ CSI_CH_HSIZE_HOR_START(0));
+ regmap_write(sdev->regmap, CSI_CH_VSIZE_REG,
+ CSI_CH_VSIZE_VER_LEN(height) |
+ CSI_CH_VSIZE_VER_START(0));
+
+ planar_offset[0] = 0;
+ switch (config->pixelformat) {
+ case V4L2_PIX_FMT_HM12:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ bytesperline_y = width;
+ bytesperline_c = width;
+ planar_offset[1] = bytesperline_y * height;
+ planar_offset[2] = -1;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ bytesperline_y = width;
+ bytesperline_c = width / 2;
+ planar_offset[1] = bytesperline_y * height;
+ planar_offset[2] = planar_offset[1] +
+ bytesperline_c * height / 2;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ bytesperline_y = width;
+ bytesperline_c = width / 2;
+ planar_offset[1] = bytesperline_y * height;
+ planar_offset[2] = planar_offset[1] +
+ bytesperline_c * height;
+ break;
+ default: /* raw */
+ bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
+ config->width) / 8;
+ bytesperline_c = 0;
+ planar_offset[1] = -1;
+ planar_offset[2] = -1;
+ break;
+ }
+
+ regmap_write(sdev->regmap, CSI_CH_BUF_LEN_REG,
+ CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
+ CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
+}
+
+int sun6i_csi_update_config(struct sun6i_csi *csi,
+ struct sun6i_csi_config *config)
+{
+ struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+
+ if (config == NULL)
+ return -EINVAL;
+
+ memcpy(&csi->config, config, sizeof(csi->config));
+
+ sun6i_csi_setup_bus(sdev);
+ sun6i_csi_set_format(sdev);
+ sun6i_csi_set_window(sdev);
+
+ return 0;
+}
+
+void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr)
+{
+ struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+ /* transform physical address to bus address */
+ dma_addr_t bus_addr = addr - PHYS_OFFSET;
+
+ regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG,
+ (bus_addr + sdev->planar_offset[0]) >> 2);
+ if (sdev->planar_offset[1] != -1)
+ regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
+ (bus_addr + sdev->planar_offset[1]) >> 2);
+ if (sdev->planar_offset[2] != -1)
+ regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
+ (bus_addr + sdev->planar_offset[2]) >> 2);
+}
+
+void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
+{
+ struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+ struct regmap *regmap = sdev->regmap;
+
+ if (!enable) {
+ regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
+ regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
+ return;
+ }
+
+ regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF);
+ regmap_write(regmap, CSI_CH_INT_EN_REG,
+ CSI_CH_INT_EN_HB_OF_INT_EN |
+ CSI_CH_INT_EN_FIFO2_OF_INT_EN |
+ CSI_CH_INT_EN_FIFO1_OF_INT_EN |
+ CSI_CH_INT_EN_FIFO0_OF_INT_EN |
+ CSI_CH_INT_EN_FD_INT_EN |
+ CSI_CH_INT_EN_CD_INT_EN);
+
+ regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
+ CSI_CAP_CH0_VCAP_ON);
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Controller and V4L2
+ */
+static int sun6i_csi_link_entity(struct sun6i_csi *csi,
+ struct media_entity *entity)
+{
+ struct media_entity *sink;
+ struct media_pad *sink_pad;
+ int ret;
+ int i;
+
+ if (!entity->num_pads) {
+ dev_err(csi->dev, "%s: invalid entity\n", entity->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < entity->num_pads; i++) {
+ if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+
+ if (i == entity->num_pads) {
+ dev_err(csi->dev, "%s: no source pad in external entity %s\n",
+ __func__, entity->name);
+ return -EINVAL;
+ }
+
+ sink = &csi->video.vdev.entity;
+ sink_pad = &csi->video.pad;
+
+ dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
+ entity->name, i, sink->name, sink_pad->index);
+ ret = media_create_pad_link(entity, i, sink, sink_pad->index,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret < 0) {
+ dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
+ entity->name, i, sink->name, sink_pad->index);
+ return ret;
+ }
+
+ return media_entity_call(sink, link_setup, sink_pad, &entity->pads[i],
+ MEDIA_LNK_FL_ENABLED);
+}
+
+static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
+ notifier);
+ struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ dev_dbg(csi->dev, "notify complete, all subdevs registered\n");
+
+ if (notifier->num_subdevs != 1)
+ return -EINVAL;
+
+ sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
+ if (sd == NULL)
+ return -EINVAL;
+
+ ret = sun6i_csi_link_entity(csi, &sd->entity);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ return media_device_register(&csi->media_dev);
+}
+
+static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
+ .complete = sun6i_subdev_notify_complete,
+};
+
+static int sun6i_csi_fwnode_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct sun6i_csi *csi = dev_get_drvdata(dev);
+
+ if (vep->base.port || vep->base.id) {
+ dev_warn(dev, "Only support a single port with one endpoint\n");
+ return -ENOTCONN;
+ }
+
+ switch (vep->bus_type) {
+ case V4L2_MBUS_PARALLEL:
+ case V4L2_MBUS_BT656:
+ csi->v4l2_ep = *vep;
+ return 0;
+ default:
+ dev_err(dev, "Unsupported media bus type\n");
+ return -ENOTCONN;
+ }
+}
+
+static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi)
+{
+ v4l2_async_notifier_cleanup(&csi->notifier);
+ v4l2_async_notifier_unregister(&csi->notifier);
+ sun6i_video_cleanup(&csi->video);
+ v4l2_device_unregister(&csi->v4l2_dev);
+ media_device_unregister(&csi->media_dev);
+ media_device_cleanup(&csi->media_dev);
+}
+
+static int sun6i_csi_v4l2_init(struct sun6i_csi *csi)
+{
+ int ret;
+
+ csi->media_dev.dev = csi->dev;
+ strlcpy(csi->media_dev.model, "Allwinner Video Capture Device",
+ sizeof(csi->media_dev.model));
+ csi->media_dev.hw_revision = 0;
+
+ media_device_init(&csi->media_dev);
+
+ csi->v4l2_dev.mdev = &csi->media_dev;
+ ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
+ if (ret) {
+ dev_err(csi->dev, "V4L2 device registration failed (%d)\n",
+ ret);
+ goto clean_media;
+ }
+
+ ret = sun6i_video_init(&csi->video, csi, "sun6i-csi");
+ if (ret)
+ goto unreg_v4l2;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints(
+ csi->dev, &csi->notifier, sizeof(struct v4l2_async_subdev),
+ sun6i_csi_fwnode_parse);
+ if (ret)
+ goto clean_video;
+
+ csi->notifier.ops = &sun6i_csi_async_ops;
+
+ ret = v4l2_async_notifier_register(&csi->v4l2_dev, &csi->notifier);
+ if (ret) {
+ dev_err(csi->dev, "notifier registration failed\n");
+ goto clean_notifier;
+ }
+
+ return 0;
+
+clean_notifier:
+ v4l2_async_notifier_cleanup(&csi->notifier);
+clean_video:
+ sun6i_video_cleanup(&csi->video);
+unreg_v4l2:
+ v4l2_device_unregister(&csi->v4l2_dev);
+clean_media:
+ media_device_cleanup(&csi->media_dev);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Resources and IRQ
+ */
+static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
+{
+ struct sun6i_csi_dev *sdev = (struct sun6i_csi_dev *)dev_id;
+ struct regmap *regmap = sdev->regmap;
+ u32 status;
+
+ regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
+
+ if (!(status & 0xFF))
+ return IRQ_NONE;
+
+ if ((status & CSI_CH_INT_STA_FIFO0_OF_PD) ||
+ (status & CSI_CH_INT_STA_FIFO1_OF_PD) ||
+ (status & CSI_CH_INT_STA_FIFO2_OF_PD) ||
+ (status & CSI_CH_INT_STA_HB_OF_PD)) {
+ regmap_write(regmap, CSI_CH_INT_STA_REG, status);
+ regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
+ regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN,
+ CSI_EN_CSI_EN);
+ return IRQ_HANDLED;
+ }
+
+ if (status & CSI_CH_INT_STA_FD_PD)
+ sun6i_video_frame_done(&sdev->csi.video);
+
+ regmap_write(regmap, CSI_CH_INT_STA_REG, status);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config sun6i_csi_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x1000,
+};
+
+static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *io_base;
+ int ret;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ sdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", io_base,
+ &sun6i_csi_regmap_config);
+ if (IS_ERR(sdev->regmap)) {
+ dev_err(&pdev->dev, "Failed to init register map\n");
+ return PTR_ERR(sdev->regmap);
+ }
+
+ sdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
+ if (IS_ERR(sdev->clk_mod)) {
+ dev_err(&pdev->dev, "Unable to acquire csi clock\n");
+ return PTR_ERR(sdev->clk_mod);
+ }
+
+ sdev->clk_ram = devm_clk_get(&pdev->dev, "ram");
+ if (IS_ERR(sdev->clk_ram)) {
+ dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n");
+ return PTR_ERR(sdev->clk_ram);
+ }
+
+ sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
+ if (IS_ERR(sdev->rstc_bus)) {
+ dev_err(&pdev->dev, "Cannot get reset controller\n");
+ return PTR_ERR(sdev->rstc_bus);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No csi IRQ specified\n");
+ ret = -ENXIO;
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, sun6i_csi_isr, 0, MODULE_NAME,
+ sdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot request csi IRQ\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int sun6i_csi_probe(struct platform_device *pdev)
+{
+ struct sun6i_csi_dev *sdev;
+ int ret;
+
+ sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return -ENOMEM;
+
+ sdev->dev = &pdev->dev;
+
+ ret = sun6i_csi_resource_request(sdev, pdev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, sdev);
+
+ sdev->csi.dev = &pdev->dev;
+ ret = sun6i_csi_v4l2_init(&sdev->csi);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sun6i_csi_remove(struct platform_device *pdev)
+{
+ struct sun6i_csi_dev *sdev = platform_get_drvdata(pdev);
+
+ sun6i_csi_v4l2_cleanup(&sdev->csi);
+
+ return 0;
+}
+
+static const struct of_device_id sun6i_csi_of_match[] = {
+ { .compatible = "allwinner,sun8i-v3s-csi", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
+
+static struct platform_driver sun6i_csi_platform_driver = {
+ .probe = sun6i_csi_probe,
+ .remove = sun6i_csi_remove,
+ .driver = {
+ .name = MODULE_NAME,
+ .of_match_table = of_match_ptr(sun6i_csi_of_match),
+ },
+};
+module_platform_driver(sun6i_csi_platform_driver);
+
+MODULE_DESCRIPTION("Allwinner V3s Camera Sensor Interface driver");
+MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
new file mode 100644
index 0000000..6733a1e
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2017 Yong Deng <yong.deng@magewell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SUN6I_CSI_H__
+#define __SUN6I_CSI_H__
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "sun6i_video.h"
+
+struct sun6i_csi;
+
+/**
+ * struct sun6i_csi_config - configs for sun6i csi
+ * @pixelformat: v4l2 pixel format (V4L2_PIX_FMT_*)
+ * @code: media bus format code (MEDIA_BUS_FMT_*)
+ * @field: used interlacing type (enum v4l2_field)
+ * @width: frame width
+ * @height: frame height
+ */
+struct sun6i_csi_config {
+ u32 pixelformat;
+ u32 code;
+ u32 field;
+ u32 width;
+ u32 height;
+};
+
+struct sun6i_csi {
+ struct device *dev;
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+
+ struct v4l2_async_notifier notifier;
+
+ /* video port settings */
+ struct v4l2_fwnode_endpoint v4l2_ep;
+
+ struct sun6i_csi_config config;
+
+ struct sun6i_video video;
+};
+
+/**
+ * sun6i_csi_get_supported_pixformats() - get csi supported pixformats
+ * @csi: pointer to the csi
+ * @pixformats: supported pixformats return from csi
+ *
+ * @return the count of pixformats or error(< 0)
+ */
+int sun6i_csi_get_supported_pixformats(struct sun6i_csi *csi,
+ const u32 **pixformats);
+
+/**
+ * sun6i_csi_is_format_support() - check if the format supported by csi
+ * @csi: pointer to the csi
+ * @pixformat: v4l2 pixel format (V4L2_PIX_FMT_*)
+ * @mbus_code: media bus format code (MEDIA_BUS_FMT_*)
+ */
+bool sun6i_csi_is_format_support(struct sun6i_csi *csi, u32 pixformat,
+ u32 mbus_code);
+
+/**
+ * sun6i_csi_set_power() - power on/off the csi
+ * @csi: pointer to the csi
+ * @enable: on/off
+ */
+int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable);
+
+/**
+ * sun6i_csi_update_config() - update the csi register setttings
+ * @csi: pointer to the csi
+ * @config: see struct sun6i_csi_config
+ */
+int sun6i_csi_update_config(struct sun6i_csi *csi,
+ struct sun6i_csi_config *config);
+
+/**
+ * sun6i_csi_update_buf_addr() - update the csi frame buffer address
+ * @csi: pointer to the csi
+ * @addr: frame buffer's physical address
+ */
+void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr);
+
+/**
+ * sun6i_csi_set_stream() - start/stop csi streaming
+ * @csi: pointer to the csi
+ * @enable: start/stop
+ */
+void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable);
+
+/* get bpp form v4l2 pixformat */
+static inline int sun6i_csi_get_bpp(unsigned int pixformat)
+{
+ switch (pixformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ return 8;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ return 10;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_HM12:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ return 12;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_YUV422P:
+ return 16;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ return 24;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ return 32;
+ }
+
+ return 0;
+}
+
+#endif /* __SUN6I_CSI_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
new file mode 100644
index 0000000..aad674a
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2017 Yong Deng <yong.deng@magewell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SUN6I_CSI_REG_H__
+#define __SUN6I_CSI_REG_H__
+
+#include <linux/kernel.h>
+
+#define CSI_EN_REG 0x0
+#define CSI_EN_VER_EN BIT(30)
+#define CSI_EN_CSI_EN BIT(0)
+
+#define CSI_IF_CFG_REG 0x4
+#define CSI_IF_CFG_SRC_TYPE_MASK BIT(21)
+#define CSI_IF_CFG_SRC_TYPE_PROGRESSED ((0 << 21) & CSI_IF_CFG_SRC_TYPE_MASK)
+#define CSI_IF_CFG_SRC_TYPE_INTERLACED ((1 << 21) & CSI_IF_CFG_SRC_TYPE_MASK)
+#define CSI_IF_CFG_FPS_DS_EN BIT(20)
+#define CSI_IF_CFG_FIELD_MASK BIT(19)
+#define CSI_IF_CFG_FIELD_NEGATIVE ((0 << 19) & CSI_IF_CFG_FIELD_MASK)
+#define CSI_IF_CFG_FIELD_POSITIVE ((1 << 19) & CSI_IF_CFG_FIELD_MASK)
+#define CSI_IF_CFG_VREF_POL_MASK BIT(18)
+#define CSI_IF_CFG_VREF_POL_NEGATIVE ((0 << 18) & CSI_IF_CFG_VREF_POL_MASK)
+#define CSI_IF_CFG_VREF_POL_POSITIVE ((1 << 18) & CSI_IF_CFG_VREF_POL_MASK)
+#define CSI_IF_CFG_HREF_POL_MASK BIT(17)
+#define CSI_IF_CFG_HREF_POL_NEGATIVE ((0 << 17) & CSI_IF_CFG_HREF_POL_MASK)
+#define CSI_IF_CFG_HREF_POL_POSITIVE ((1 << 17) & CSI_IF_CFG_HREF_POL_MASK)
+#define CSI_IF_CFG_CLK_POL_MASK BIT(16)
+#define CSI_IF_CFG_CLK_POL_RISING_EDGE ((0 << 16) & CSI_IF_CFG_CLK_POL_MASK)
+#define CSI_IF_CFG_CLK_POL_FALLING_EDGE ((1 << 16) & CSI_IF_CFG_CLK_POL_MASK)
+#define CSI_IF_CFG_IF_DATA_WIDTH_MASK GENMASK(10, 8)
+#define CSI_IF_CFG_IF_DATA_WIDTH_8BIT ((0 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
+#define CSI_IF_CFG_IF_DATA_WIDTH_10BIT ((1 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
+#define CSI_IF_CFG_IF_DATA_WIDTH_12BIT ((2 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
+#define CSI_IF_CFG_MIPI_IF_MASK BIT(7)
+#define CSI_IF_CFG_MIPI_IF_CSI (0 << 7)
+#define CSI_IF_CFG_MIPI_IF_MIPI (1 << 7)
+#define CSI_IF_CFG_CSI_IF_MASK GENMASK(4, 0)
+#define CSI_IF_CFG_CSI_IF_YUV422_INTLV ((0 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+#define CSI_IF_CFG_CSI_IF_YUV422_16BIT ((1 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+#define CSI_IF_CFG_CSI_IF_BT656 ((4 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+#define CSI_IF_CFG_CSI_IF_BT1120 ((5 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+
+#define CSI_CAP_REG 0x8
+#define CSI_CAP_CH0_CAP_MASK_MASK GENMASK(5, 2)
+#define CSI_CAP_CH0_CAP_MASK(count) ((count << 2) & CSI_CAP_CH0_CAP_MASK_MASK)
+#define CSI_CAP_CH0_VCAP_ON BIT(1)
+#define CSI_CAP_CH0_SCAP_ON BIT(0)
+
+#define CSI_SYNC_CNT_REG 0xc
+#define CSI_FIFO_THRS_REG 0x10
+#define CSI_BT656_HEAD_CFG_REG 0x14
+#define CSI_PTN_LEN_REG 0x30
+#define CSI_PTN_ADDR_REG 0x34
+#define CSI_VER_REG 0x3c
+
+#define CSI_CH_CFG_REG 0x44
+#define CSI_CH_CFG_INPUT_FMT_MASK GENMASK(23, 20)
+#define CSI_CH_CFG_INPUT_FMT(fmt) ((fmt << 20) & CSI_CH_CFG_INPUT_FMT_MASK)
+#define CSI_CH_CFG_OUTPUT_FMT_MASK GENMASK(19, 16)
+#define CSI_CH_CFG_OUTPUT_FMT(fmt) ((fmt << 16) & CSI_CH_CFG_OUTPUT_FMT_MASK)
+#define CSI_CH_CFG_VFLIP_EN BIT(13)
+#define CSI_CH_CFG_HFLIP_EN BIT(12)
+#define CSI_CH_CFG_FIELD_SEL_MASK GENMASK(11, 10)
+#define CSI_CH_CFG_FIELD_SEL_FIELD0 ((0 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
+#define CSI_CH_CFG_FIELD_SEL_FIELD1 ((1 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
+#define CSI_CH_CFG_FIELD_SEL_BOTH ((2 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
+#define CSI_CH_CFG_INPUT_SEQ_MASK GENMASK(9, 8)
+#define CSI_CH_CFG_INPUT_SEQ(seq) ((seq << 8) & CSI_CH_CFG_INPUT_SEQ_MASK)
+
+#define CSI_CH_SCALE_REG 0x4c
+#define CSI_CH_SCALE_QUART_EN BIT(0)
+
+#define CSI_CH_F0_BUFA_REG 0x50
+
+#define CSI_CH_F1_BUFA_REG 0x58
+
+#define CSI_CH_F2_BUFA_REG 0x60
+
+#define CSI_CH_STA_REG 0x6c
+#define CSI_CH_STA_FIELD_STA_MASK BIT(2)
+#define CSI_CH_STA_FIELD_STA_FIELD0 ((0 << 2) & CSI_CH_STA_FIELD_STA_MASK)
+#define CSI_CH_STA_FIELD_STA_FIELD1 ((1 << 2) & CSI_CH_STA_FIELD_STA_MASK)
+#define CSI_CH_STA_VCAP_STA BIT(1)
+#define CSI_CH_STA_SCAP_STA BIT(0)
+
+#define CSI_CH_INT_EN_REG 0x70
+#define CSI_CH_INT_EN_VS_INT_EN BIT(7)
+#define CSI_CH_INT_EN_HB_OF_INT_EN BIT(6)
+#define CSI_CH_INT_EN_MUL_ERR_INT_EN BIT(5)
+#define CSI_CH_INT_EN_FIFO2_OF_INT_EN BIT(4)
+#define CSI_CH_INT_EN_FIFO1_OF_INT_EN BIT(3)
+#define CSI_CH_INT_EN_FIFO0_OF_INT_EN BIT(2)
+#define CSI_CH_INT_EN_FD_INT_EN BIT(1)
+#define CSI_CH_INT_EN_CD_INT_EN BIT(0)
+
+#define CSI_CH_INT_STA_REG 0x74
+#define CSI_CH_INT_STA_VS_PD BIT(7)
+#define CSI_CH_INT_STA_HB_OF_PD BIT(6)
+#define CSI_CH_INT_STA_MUL_ERR_PD BIT(5)
+#define CSI_CH_INT_STA_FIFO2_OF_PD BIT(4)
+#define CSI_CH_INT_STA_FIFO1_OF_PD BIT(3)
+#define CSI_CH_INT_STA_FIFO0_OF_PD BIT(2)
+#define CSI_CH_INT_STA_FD_PD BIT(1)
+#define CSI_CH_INT_STA_CD_PD BIT(0)
+
+#define CSI_CH_FLD1_VSIZE_REG 0x78
+
+#define CSI_CH_HSIZE_REG 0x80
+#define CSI_CH_HSIZE_HOR_LEN_MASK GENMASK(28, 16)
+#define CSI_CH_HSIZE_HOR_LEN(len) ((len << 16) & CSI_CH_HSIZE_HOR_LEN_MASK)
+#define CSI_CH_HSIZE_HOR_START_MASK GENMASK(12, 0)
+#define CSI_CH_HSIZE_HOR_START(start) ((start << 0) & CSI_CH_HSIZE_HOR_START_MASK)
+
+#define CSI_CH_VSIZE_REG 0x84
+#define CSI_CH_VSIZE_VER_LEN_MASK GENMASK(28, 16)
+#define CSI_CH_VSIZE_VER_LEN(len) ((len << 16) & CSI_CH_VSIZE_VER_LEN_MASK)
+#define CSI_CH_VSIZE_VER_START_MASK GENMASK(12, 0)
+#define CSI_CH_VSIZE_VER_START(start) ((start << 0) & CSI_CH_VSIZE_VER_START_MASK)
+
+#define CSI_CH_BUF_LEN_REG 0x88
+#define CSI_CH_BUF_LEN_BUF_LEN_C_MASK GENMASK(29, 16)
+#define CSI_CH_BUF_LEN_BUF_LEN_C(len) ((len << 16) & CSI_CH_BUF_LEN_BUF_LEN_C_MASK)
+#define CSI_CH_BUF_LEN_BUF_LEN_Y_MASK GENMASK(13, 0)
+#define CSI_CH_BUF_LEN_BUF_LEN_Y(len) ((len << 0) & CSI_CH_BUF_LEN_BUF_LEN_Y_MASK)
+
+#define CSI_CH_FLIP_SIZE_REG 0x8c
+#define CSI_CH_FLIP_SIZE_VER_LEN_MASK GENMASK(28, 16)
+#define CSI_CH_FLIP_SIZE_VER_LEN(len) ((len << 16) & CSI_CH_FLIP_SIZE_VER_LEN_MASK)
+#define CSI_CH_FLIP_SIZE_VALID_LEN_MASK GENMASK(12, 0)
+#define CSI_CH_FLIP_SIZE_VALID_LEN(len) ((len << 0) & CSI_CH_FLIP_SIZE_VALID_LEN_MASK)
+
+#define CSI_CH_FRM_CLK_CNT_REG 0x90
+#define CSI_CH_ACC_ITNL_CLK_CNT_REG 0x94
+#define CSI_CH_FIFO_STAT_REG 0x98
+#define CSI_CH_PCLK_STAT_REG 0x9c
+
+/*
+ * csi input data format
+ */
+enum csi_input_fmt {
+ CSI_INPUT_FORMAT_RAW = 0,
+ CSI_INPUT_FORMAT_YUV422 = 3,
+ CSI_INPUT_FORMAT_YUV420 = 4,
+};
+
+/*
+ * csi output data format
+ */
+enum csi_output_fmt {
+ /* only when input format is RAW */
+ CSI_FIELD_RAW_8 = 0,
+ CSI_FIELD_RAW_10 = 1,
+ CSI_FIELD_RAW_12 = 2,
+ CSI_FIELD_RGB565 = 4,
+ CSI_FIELD_RGB888 = 5,
+ CSI_FIELD_PRGB888 = 6,
+ CSI_FRAME_RAW_8 = 8,
+ CSI_FRAME_RAW_10 = 9,
+ CSI_FRAME_RAW_12 = 10,
+ CSI_FRAME_RGB565 = 12,
+ CSI_FRAME_RGB888 = 13,
+ CSI_FRAME_PRGB888 = 14,
+
+ /* only when input format is YUV422 */
+ CSI_FIELD_PLANAR_YUV422 = 0,
+ CSI_FIELD_PLANAR_YUV420 = 1,
+ CSI_FRAME_PLANAR_YUV420 = 2,
+ CSI_FRAME_PLANAR_YUV422 = 3,
+ CSI_FIELD_UV_CB_YUV422 = 4,
+ CSI_FIELD_UV_CB_YUV420 = 5,
+ CSI_FRAME_UV_CB_YUV420 = 6,
+ CSI_FRAME_UV_CB_YUV422 = 7,
+ CSI_FIELD_MB_YUV422 = 8,
+ CSI_FIELD_MB_YUV420 = 9,
+ CSI_FRAME_MB_YUV420 = 10,
+ CSI_FRAME_MB_YUV422 = 11,
+ CSI_FIELD_UV_CB_YUV422_10 = 12,
+ CSI_FIELD_UV_CB_YUV420_10 = 13,
+};
+
+/*
+ * csi YUV input data sequence
+ */
+enum csi_input_seq {
+ /* only when input format is YUV422 */
+ CSI_INPUT_SEQ_YUYV = 0,
+ CSI_INPUT_SEQ_YVYU,
+ CSI_INPUT_SEQ_UYVY,
+ CSI_INPUT_SEQ_VYUY,
+};
+
+#endif /* __SUN6I_CSI_REG_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
new file mode 100644
index 0000000..2b683ac
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2017 Magewell Electronics Co., Ltd. (Nanjing).
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.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.
+ *
+ * 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/of.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun6i_csi.h"
+#include "sun6i_video.h"
+
+struct sun6i_csi_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+
+ dma_addr_t dma_addr;
+ bool queued_to_csi;
+};
+
+static struct sun6i_csi_format *
+find_format_by_pixformat(struct sun6i_video *video, unsigned int pixformat)
+{
+ unsigned int num_formats = video->num_formats;
+ struct sun6i_csi_format *fmt;
+ unsigned int i;
+
+ for (i = 0; i < num_formats; i++) {
+ fmt = &video->formats[i];
+ if (fmt->pixformat == pixformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static struct v4l2_subdev *
+sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_pad(&video->pad);
+
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int sun6i_video_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct sun6i_video *video = vb2_get_drv_priv(vq);
+ unsigned int size = video->fmt.fmt.pix.sizeimage;
+
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int sun6i_video_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct sun6i_csi_buffer *buf =
+ container_of(vbuf, struct sun6i_csi_buffer, vb);
+ struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = video->fmt.fmt.pix.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ v4l2_err(video->vdev.v4l2_dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ vbuf->field = video->fmt.fmt.pix.field;
+
+ return 0;
+}
+
+static int sun6i_pipeline_set_stream(struct sun6i_video *video, bool enable)
+{
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ entity = &video->vdev.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, enable);
+ if (enable && ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct sun6i_video *video = vb2_get_drv_priv(vq);
+ struct sun6i_csi_buffer *buf;
+ struct sun6i_csi_buffer *next_buf;
+ struct sun6i_csi_config config;
+ unsigned long flags;
+ int ret;
+
+ video->sequence = 0;
+
+ ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe);
+ if (ret < 0)
+ goto clear_dma_queue;
+
+ config.pixelformat = video->fmt.fmt.pix.pixelformat;
+ config.code = video->current_fmt->mbus_code;
+ config.field = video->fmt.fmt.pix.field;
+ config.width = video->fmt.fmt.pix.width;
+ config.height = video->fmt.fmt.pix.height;
+
+ ret = sun6i_csi_update_config(video->csi, &config);
+ if (ret < 0)
+ goto stop_media_pipeline;
+
+ spin_lock_irqsave(&video->dma_queue_lock, flags);
+
+ buf = list_first_entry(&video->dma_queue,
+ struct sun6i_csi_buffer, list);
+ buf->queued_to_csi = true;
+ sun6i_csi_update_buf_addr(video->csi, buf->dma_addr);
+
+ sun6i_csi_set_stream(video->csi, true);
+
+ /*
+ * CSI will lookup the next dma buffer for next frame before the
+ * the current frame done IRQ triggered. This is not documented
+ * but reported by Ond?ej Jirman.
+ * The BSP code has workaround for this too. It skip to mark the
+ * first buffer as frame done for VB2 and pass the second buffer
+ * to CSI in the first frame done ISR call. Then in second frame
+ * done ISR call, it mark the first buffer as frame done for VB2
+ * and pass the third buffer to CSI. And so on. The bad thing is
+ * that the first buffer will be written twice and the first frame
+ * is dropped even the queued buffer is sufficient.
+ * So, I make some improvement here. Pass the next buffer to CSI
+ * just follow starting the CSI. In this case, the first frame
+ * will be stored in first buffer, second frame in second buffer.
+ * This mothed is used to avoid dropping the first frame, it
+ * would also drop frame when lacking of queued buffer.
+ */
+ next_buf = list_next_entry(buf, list);
+ next_buf->queued_to_csi = true;
+ sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
+
+ spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+
+ ret = sun6i_pipeline_set_stream(video, true);
+ if (ret < 0)
+ goto stop_csi_stream;
+
+ return 0;
+
+stop_csi_stream:
+ sun6i_csi_set_stream(video->csi, false);
+stop_media_pipeline:
+ media_pipeline_stop(&video->vdev.entity);
+clear_dma_queue:
+ spin_lock_irqsave(&video->dma_queue_lock, flags);
+ list_for_each_entry(buf, &video->dma_queue, list)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ INIT_LIST_HEAD(&video->dma_queue);
+ spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+
+ return ret;
+}
+
+static void sun6i_video_stop_streaming(struct vb2_queue *vq)
+{
+ struct sun6i_video *video = vb2_get_drv_priv(vq);
+ unsigned long flags;
+ struct sun6i_csi_buffer *buf;
+
+ sun6i_pipeline_set_stream(video, false);
+
+ sun6i_csi_set_stream(video->csi, false);
+
+ media_pipeline_stop(&video->vdev.entity);
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&video->dma_queue_lock, flags);
+ list_for_each_entry(buf, &video->dma_queue, list)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ INIT_LIST_HEAD(&video->dma_queue);
+ spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+}
+
+static void sun6i_video_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct sun6i_csi_buffer *buf =
+ container_of(vbuf, struct sun6i_csi_buffer, vb);
+ struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags;
+
+ spin_lock_irqsave(&video->dma_queue_lock, flags);
+ buf->queued_to_csi = false;
+ list_add_tail(&buf->list, &video->dma_queue);
+ spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+}
+
+void sun6i_video_frame_done(struct sun6i_video *video)
+{
+ struct sun6i_csi_buffer *buf;
+ struct sun6i_csi_buffer *next_buf;
+ struct vb2_v4l2_buffer *vbuf;
+
+ spin_lock(&video->dma_queue_lock);
+
+ buf = list_first_entry(&video->dma_queue,
+ struct sun6i_csi_buffer, list);
+ if (list_is_last(&buf->list, &video->dma_queue)) {
+ dev_dbg(video->csi->dev, "Frame droped!\n");
+ goto unlock;
+ }
+
+ next_buf = list_next_entry(buf, list);
+ /* If a new buffer (#next_buf) had not been queued to CSI, the old
+ * buffer (#buf) is still holding by CSI for storing the next
+ * frame. So, we queue a new buffer (#next_buf) to CSI then wait
+ * for next ISR call.
+ */
+ if (!next_buf->queued_to_csi) {
+ next_buf->queued_to_csi = true;
+ sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
+ dev_dbg(video->csi->dev, "Frame droped!\n");
+ goto unlock;
+ }
+
+ list_del(&buf->list);
+ vbuf = &buf->vb;
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
+ vbuf->sequence = video->sequence;
+ vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
+
+ /* Prepare buffer for next frame but one. */
+ if (!list_is_last(&next_buf->list, &video->dma_queue)) {
+ next_buf = list_next_entry(next_buf, list);
+ next_buf->queued_to_csi = true;
+ sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
+ } else {
+ dev_dbg(video->csi->dev, "Next frame will be droped!\n");
+ }
+
+unlock:
+ video->sequence++;
+ spin_unlock(&video->dma_queue_lock);
+}
+
+static struct vb2_ops sun6i_csi_vb2_ops = {
+ .queue_setup = sun6i_video_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_prepare = sun6i_video_buffer_prepare,
+ .start_streaming = sun6i_video_start_streaming,
+ .stop_streaming = sun6i_video_stop_streaming,
+ .buf_queue = sun6i_video_buffer_queue,
+};
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sun6i_video *video = video_drvdata(file);
+
+ strlcpy(cap->driver, "sun6i-video", sizeof(cap->driver));
+ strlcpy(cap->card, video->vdev.name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ video->csi->dev->of_node->name);
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct sun6i_video *video = video_drvdata(file);
+ u32 index = f->index;
+
+ if (index >= video->num_formats)
+ return -EINVAL;
+
+ f->pixelformat = video->formats[index].pixformat;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct sun6i_video *video = video_drvdata(file);
+
+ *fmt = video->fmt;
+
+ return 0;
+}
+
+static int sun6i_video_try_fmt(struct sun6i_video *video, struct v4l2_format *f,
+ struct sun6i_csi_format **current_fmt)
+{
+ struct sun6i_csi_format *csi_fmt;
+ struct v4l2_pix_format *pixfmt = &f->fmt.pix;
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = sun6i_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -ENXIO;
+
+ csi_fmt = find_format_by_pixformat(video, pixfmt->pixelformat);
+ if (csi_fmt == NULL) {
+ if (video->num_formats > 0) {
+ csi_fmt = &video->formats[0];
+ pixfmt->pixelformat = csi_fmt->pixformat;
+ } else
+ return -EINVAL;
+ }
+
+ format.pad = pad;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ v4l2_fill_mbus_format(&format.format, pixfmt, csi_fmt->mbus_code);
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+ if (ret)
+ return ret;
+
+ v4l2_fill_pix_format(pixfmt, &format.format);
+
+ pixfmt->bytesperline = (pixfmt->width * csi_fmt->bpp) >> 3;
+ pixfmt->sizeimage = (pixfmt->width * csi_fmt->bpp * pixfmt->height) / 8;
+
+ if (current_fmt)
+ *current_fmt = csi_fmt;
+
+ return 0;
+}
+
+static int sun6i_video_set_fmt(struct sun6i_video *video, struct v4l2_format *f)
+{
+ struct v4l2_subdev_format format;
+ struct sun6i_csi_format *current_fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = sun6i_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -ENXIO;
+
+ ret = sun6i_video_try_fmt(video, f, ¤t_fmt);
+ if (ret)
+ return ret;
+
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
+ current_fmt->mbus_code);
+ ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ video->fmt = *f;
+ video->current_fmt = current_fmt;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct sun6i_video *video = video_drvdata(file);
+
+ if (vb2_is_busy(&video->vb2_vidq))
+ return -EBUSY;
+
+ return sun6i_video_set_fmt(video, f);
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct sun6i_video *video = video_drvdata(file);
+
+ return sun6i_video_try_fmt(video, f, NULL);
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ struct sun6i_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ subdev = sun6i_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -ENXIO;
+
+ ret = v4l2_subdev_call(subdev, video, g_input_status, &inp->status);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ if (v4l2_subdev_has_op(subdev, pad, dv_timings_cap)) {
+ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ inp->std = 0;
+ } else {
+ inp->capabilities = 0;
+ inp->std = 0;
+ }
+
+ strlcpy(inp->name, subdev->name, sizeof(inp->name));
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_input = vidioc_g_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+static int sun6i_video_open(struct file *file)
+{
+ struct sun6i_video *video = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&video->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_pipeline_pm_use(&video->vdev.entity, 1);
+ if (ret < 0)
+ goto fh_release;
+
+ /* check if already powered */
+ if (!v4l2_fh_is_singular_file(file))
+ goto unlock;
+
+ ret = sun6i_csi_set_power(video->csi, true);
+ if (ret < 0)
+ goto fh_release;
+
+ mutex_unlock(&video->lock);
+ return 0;
+
+fh_release:
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+static int sun6i_video_close(struct file *file)
+{
+ struct sun6i_video *video = video_drvdata(file);
+ bool last_fh;
+
+ mutex_lock(&video->lock);
+
+ last_fh = v4l2_fh_is_singular_file(file);
+
+ _vb2_fop_release(file, NULL);
+
+ v4l2_pipeline_pm_use(&video->vdev.entity, 0);
+
+ if (last_fh)
+ sun6i_csi_set_power(video->csi, false);
+
+ mutex_unlock(&video->lock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations sun6i_video_fops = {
+ .owner = THIS_MODULE,
+ .open = sun6i_video_open,
+ .release = sun6i_video_close,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll
+};
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+static int sun6i_video_formats_init(struct sun6i_video *video)
+{
+ struct v4l2_subdev_mbus_code_enum mbus_code = { 0 };
+ struct sun6i_csi *csi = video->csi;
+ struct v4l2_format format;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ const u32 *pixformats;
+ int pixformat_count = 0;
+ u32 subdev_codes[32]; /* subdev format codes, 32 should be enough */
+ int codes_count = 0;
+ int num_fmts = 0;
+ int i, j;
+
+ subdev = sun6i_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -ENXIO;
+
+ /* Get supported pixformats of CSI */
+ pixformat_count = sun6i_csi_get_supported_pixformats(csi, &pixformats);
+ if (pixformat_count <= 0)
+ return -ENXIO;
+
+ /* Get subdev formats codes */
+ mbus_code.pad = pad;
+ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL,
+ &mbus_code)) {
+ if (codes_count >= ARRAY_SIZE(subdev_codes)) {
+ dev_warn(video->csi->dev,
+ "subdev_codes array is full!\n");
+ break;
+ }
+ subdev_codes[codes_count] = mbus_code.code;
+ codes_count++;
+ mbus_code.index++;
+ }
+
+ if (!codes_count)
+ return -ENXIO;
+
+ /* Get supported formats count */
+ for (i = 0; i < codes_count; i++) {
+ for (j = 0; j < pixformat_count; j++) {
+ if (!sun6i_csi_is_format_support(csi, pixformats[j],
+ subdev_codes[i])) {
+ continue;
+ }
+ num_fmts++;
+ }
+ }
+
+ if (!num_fmts)
+ return -ENXIO;
+
+ video->num_formats = num_fmts;
+ video->formats = devm_kcalloc(video->csi->dev, num_fmts,
+ sizeof(struct sun6i_csi_format), GFP_KERNEL);
+ if (!video->formats)
+ return -ENOMEM;
+
+ /* Get supported formats */
+ num_fmts = 0;
+ for (i = 0; i < codes_count; i++) {
+ for (j = 0; j < pixformat_count; j++) {
+ if (!sun6i_csi_is_format_support(csi, pixformats[j],
+ subdev_codes[i])) {
+ continue;
+ }
+
+ video->formats[num_fmts].pixformat = pixformats[j];
+ video->formats[num_fmts].mbus_code = subdev_codes[i];
+ video->formats[num_fmts].bpp =
+ sun6i_csi_get_bpp(pixformats[j]);
+ num_fmts++;
+ }
+ }
+
+ /* setup default format */
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ format.fmt.pix.width = 1280;
+ format.fmt.pix.height = 720;
+ format.fmt.pix.pixelformat = video->formats[0].pixformat;
+ sun6i_video_set_fmt(video, &format);
+
+ return 0;
+}
+
+static int sun6i_video_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct video_device *vdev = media_entity_to_video_device(entity);
+ struct sun6i_video *video = video_get_drvdata(vdev);
+
+ if (WARN_ON(video == NULL))
+ return 0;
+
+ return sun6i_video_formats_init(video);
+}
+
+static const struct media_entity_operations sun6i_video_media_ops = {
+ .link_setup = sun6i_video_link_setup,
+};
+
+int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
+ const char *name)
+{
+ struct video_device *vdev = &video->vdev;
+ struct vb2_queue *vidq = &video->vb2_vidq;
+ int ret;
+
+ video->csi = csi;
+
+ /* Initialize the media entity... */
+ video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ vdev->entity.ops = &sun6i_video_media_ops;
+ ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&video->lock);
+
+ INIT_LIST_HEAD(&video->dma_queue);
+ spin_lock_init(&video->dma_queue_lock);
+
+ video->sequence = 0;
+ video->num_formats = 0;
+
+ /* Initialize videobuf2 queue */
+ vidq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidq->io_modes = VB2_MMAP | VB2_DMABUF;
+ vidq->drv_priv = video;
+ vidq->buf_struct_size = sizeof(struct sun6i_csi_buffer);
+ vidq->ops = &sun6i_csi_vb2_ops;
+ vidq->mem_ops = &vb2_dma_contig_memops;
+ vidq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vidq->lock = &video->lock;
+ /* Make sure non-dropped frame */
+ vidq->min_buffers_needed = 3;
+ vidq->dev = csi->dev;
+
+ ret = vb2_queue_init(vidq);
+ if (ret) {
+ v4l2_err(&csi->v4l2_dev, "vb2_queue_init failed: %d\n", ret);
+ goto error;
+ }
+
+ /* Register video device */
+ strlcpy(vdev->name, name, sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &sun6i_video_fops;
+ vdev->ioctl_ops = &sun6i_video_ioctl_ops;
+ vdev->vfl_type = VFL_TYPE_GRABBER;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->v4l2_dev = &csi->v4l2_dev;
+ vdev->queue = vidq;
+ vdev->lock = &video->lock;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+ video_set_drvdata(vdev, video);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ v4l2_err(&csi->v4l2_dev,
+ "video_register_device failed: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ sun6i_video_cleanup(video);
+ return ret;
+}
+
+void sun6i_video_cleanup(struct sun6i_video *video)
+{
+ if (video_is_registered(&video->vdev))
+ video_unregister_device(&video->vdev);
+
+ media_entity_cleanup(&video->vdev.entity);
+}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
new file mode 100644
index 0000000..f20928a
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Yong Deng <yong.deng@magewell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SUN6I_VIDEO_H__
+#define __SUN6I_VIDEO_H__
+
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+/*
+ * struct sun6i_csi_format - CSI media bus format information
+ * @pixformat: V4l2 pixformat for this format
+ * @mbus_code: V4L2 media bus format code.
+ * @bpp: Bytes per pixel (when stored in memory)
+ */
+struct sun6i_csi_format {
+ u32 pixformat;
+ u32 mbus_code;
+ u8 bpp;
+};
+
+struct sun6i_csi;
+
+struct sun6i_video {
+ struct video_device vdev;
+ struct media_pad pad;
+ struct sun6i_csi *csi;
+
+ struct mutex lock;
+
+ struct vb2_queue vb2_vidq;
+ spinlock_t dma_queue_lock;
+ struct list_head dma_queue;
+
+ unsigned int sequence;
+
+ struct sun6i_csi_format *formats;
+ unsigned int num_formats;
+ struct sun6i_csi_format *current_fmt;
+ struct v4l2_format fmt;
+};
+
+int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
+ const char *name);
+void sun6i_video_cleanup(struct sun6i_video *video);
+
+void sun6i_video_frame_done(struct sun6i_video *video);
+
+#endif /* __SUN6I_VIDEO_H__ */
--
1.8.3.1
^ permalink raw reply related
* [PATCH 1/1] clk: remove invalid __clk_set_parent comment in __clk_core_init
From: Dong Aisheng @ 2017-12-22 9:47 UTC (permalink / raw)
To: linux-arm-kernel
Those comments are invalid anymore. So remove them.
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
drivers/clk/clk.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d6e2d5c..4675adf 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2927,10 +2927,6 @@ static int __clk_core_init(struct clk_core *core)
struct clk_core *parent = __clk_init_parent(orphan);
unsigned long flags;
- /*
- * we could call __clk_set_parent, but that would result in a
- * redundant call to the .set_rate op, if it exists
- */
if (parent) {
/* update the clk tree topology */
flags = clk_enable_lock();
--
2.7.4
^ permalink raw reply related
* [PATCH 2/2] mtd: spi-nor: cadence-quadspi: Add support for direct access mode
From: Vignesh R @ 2017-12-22 9:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <82986b75-79a1-68da-efbd-aeb603b3d7be@wedev4u.fr>
Hi Cyrille
On Wednesday 20 December 2017 08:43 PM, Cyrille Pitchen wrote:
> Hi Vignesh,
>
> Le 07/12/2017 ? 07:38, Vignesh R a ?crit :
>> Cadence QSPI controller provides direct access mode through which flash
>> can be accessed in a memory-mapped IO mode. This enables read/write to
>> flash using memcpy*() functions. This mode provides higher throughput
>> for both read/write operations when compared to current indirect mode of
>> operation.
>>
>> This patch therefore adds support to use QSPI in direct mode. If the
>> window reserved in SoC's memory map for MMIO access is less that of
>> flash size(like on most SoCFPGA variants), then the driver falls back
>> to indirect mode of operation.
>>
>> On TI's 66AK2G SoC, with ARM running at 600MHz and QSPI at 96MHz
>> switching to direct mode improves read throughput from 3MB/s to 8MB/s.
>>
>> Signed-off-by: Vignesh R <vigneshr@ti.com>
>> ---
[...]
>> +static int cqspi_direct_read_execute(struct spi_nor *nor, u8 *rxbuf,
>> +????????????????????????????????? loff_t from_addr, const size_t len)
>> +{
>> +???? struct cqspi_flash_pdata *f_pdata = nor->priv;
>> +???? struct cqspi_st *cqspi = f_pdata->cqspi;
>> +???? u32 reg;
>> +
>> +???? reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
>> +???? reg |= CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL;
>> +???? writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
>
> I guess setting the ENB_DIR_ACC_CTRL bit could be set once for all when you
> set use_direct_mode to true, couldn't it?
>
> It may improve the read performance even more. However not expecting much
> difference for Page Program operations.
>
> Then you could call directly call mempcy_fromio() from cqspi_read().
> Not mandatory for me, since I also like the symmetry of the 2 functions:
> cqspi_direct_read_execute() / cqspi_indirect_read_execute().
>
Right, actually I can unconditionally set ENB_DIR_ACC_CTRL once in
cqspi_controller_init(). Indirect accesses will still be forwarded to
indirect access controller even if direct access controller is kept
enabled. So, indirect ops users are not affected.
I will make the changes in v2.
> So it's up to you :)
>
>> +???? memcpy_fromio(rxbuf, cqspi->ahb_base + from_addr, len);
>> +
>> +???? return 0;
>> +}
>> +
>>? static int cqspi_write_setup(struct spi_nor *nor)
>>? {
>>??????? unsigned int reg;
>> @@ -671,6 +689,21 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr,
>>??????? return ret;
>>? }
>>?
>> +static int cqspi_direct_write_execute(struct spi_nor *nor, loff_t to_addr,
>> +?????????????????????????????????? const u8 *txbuf, const size_t len)
>> +{
>> +???? struct cqspi_flash_pdata *f_pdata = nor->priv;
>> +???? struct cqspi_st *cqspi = f_pdata->cqspi;
>> +???? u32 reg;
>> +
>> +???? reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
>> +???? reg |= CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL;
>> +???? writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
>
> Same comment here.
>
>> +???? memcpy_toio(cqspi->ahb_base + to_addr, txbuf, len);
>> +
>> +???? return 0;
>> +}
>> +
>>? static void cqspi_chipselect(struct spi_nor *nor)
>>? {
>>??????? struct cqspi_flash_pdata *f_pdata = nor->priv;
>> @@ -891,6 +924,7 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
>>? static ssize_t cqspi_write(struct spi_nor *nor, loff_t to,
>>?????????????????????????? size_t len, const u_char *buf)
>>? {
>> +???? struct cqspi_flash_pdata *f_pdata = nor->priv;
>>??????? int ret;
>>?
>>??????? ret = cqspi_set_protocol(nor, 0);
>> @@ -901,7 +935,10 @@ static ssize_t cqspi_write(struct spi_nor *nor, loff_t to,
>>??????? if (ret)
>>??????????????? return ret;
>>?
>> -???? ret = cqspi_indirect_write_execute(nor, to, buf, len);
>> +???? if (f_pdata->use_direct_mode)
>> +???????????? ret = cqspi_direct_write_execute(nor, to, buf, len);
>> +???? else
>> +???????????? ret = cqspi_indirect_write_execute(nor, to, buf, len);
>>??????? if (ret)
>>??????????????? return ret;
>>?
>> @@ -911,6 +948,7 @@ static ssize_t cqspi_write(struct spi_nor *nor, loff_t to,
>>? static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
>>????????????????????????? size_t len, u_char *buf)
>>? {
>> +???? struct cqspi_flash_pdata *f_pdata = nor->priv;
>>??????? int ret;
>>?
>>??????? ret = cqspi_set_protocol(nor, 1);
>> @@ -921,7 +959,10 @@ static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
>>??????? if (ret)
>>??????????????? return ret;
>>?
>> -???? ret = cqspi_indirect_read_execute(nor, buf, from, len);
>> +???? if (f_pdata->use_direct_mode)
>> +???????????? ret = cqspi_direct_read_execute(nor, buf, from, len);
>> +???? else
>> +???????????? ret = cqspi_indirect_read_execute(nor, buf, from, len);
>>??????? if (ret)
>>??????????????? return ret;
>>?
>> @@ -1153,6 +1194,12 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>??????????????????????? goto err;
>>?
>>??????????????? f_pdata->registered = true;
>> +
>> +???????????? if (mtd->size <= cqspi->ahb_size) {
>> +???????????????????? f_pdata->use_direct_mode = true;
>> +???????????????????? dev_info(nor->dev, "using direct mode for %s\n",
>> +????????????????????????????? mtd->name);
>
> Please use dev_dbg() here insted of dev_info(). IMHO, this kind of output
> is not really needed by regular users.
>
Agreed, will update that.
> Otherwise, the series looks great!
>
Thanks for the review! Will submit v2 shortly.
--
Regards
Vignesh
^ permalink raw reply
* [PATCH 2/2] ARM: probes: avoid adding kprobes to sensitive kernel-entry/exit code
From: Russell King - ARM Linux @ 2017-12-22 9:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKaJLVtKjojgX6kf0XtePK5sYkLC29UNihqg4DW8OFrUqCfvcQ@mail.gmail.com>
On Thu, Dec 21, 2017 at 09:40:05PM +0200, Sam Protsenko wrote:
> On 25 November 2017 at 13:33, Russell King <rmk+kernel@armlinux.org.uk> wrote:
> > Avoid adding kprobes to any of the kernel entry/exit or startup
> > assembly code, or code in the identity-mapped region. This code does
> > not conform to the standard C conventions, which means that the
> > expectations of the kprobes code is not forfilled.
> >
> > Placing kprobes at some of these locations results in the kernel trying
> > to return to userspace addresses while retaining the CPU in kernel mode.
> >
> > Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> > ---
> > arch/arm/include/asm/exception.h | 3 +--
> > arch/arm/include/asm/sections.h | 21 +++++++++++++++++++++
> > arch/arm/include/asm/traps.h | 12 ------------
> > arch/arm/kernel/entry-armv.S | 6 +-----
> > arch/arm/kernel/entry-common.S | 1 +
> > arch/arm/kernel/stacktrace.c | 14 ++------------
> > arch/arm/kernel/traps.c | 4 ++--
> > arch/arm/kernel/vmlinux.lds.S | 6 +++---
> > arch/arm/mm/fault.c | 5 ++---
> > arch/arm/probes/kprobes/core.c | 14 +++++++++++---
> > 10 files changed, 44 insertions(+), 42 deletions(-)
> >
> > diff --git a/arch/arm/include/asm/exception.h b/arch/arm/include/asm/exception.h
> > index a7273ad9587a..58e039a851af 100644
> > --- a/arch/arm/include/asm/exception.h
> > +++ b/arch/arm/include/asm/exception.h
> > @@ -10,11 +10,10 @@
> >
> > #include <linux/interrupt.h>
> >
> > -#define __exception __attribute__((section(".exception.text")))
> > #ifdef CONFIG_FUNCTION_GRAPH_TRACER
> > #define __exception_irq_entry __irq_entry
> > #else
> > -#define __exception_irq_entry __exception
> > +#define __exception_irq_entry
> > #endif
> >
> > #endif /* __ASM_ARM_EXCEPTION_H */
> > diff --git a/arch/arm/include/asm/sections.h b/arch/arm/include/asm/sections.h
> > index 63dfe1f10335..4ceb4f757d4d 100644
> > --- a/arch/arm/include/asm/sections.h
> > +++ b/arch/arm/include/asm/sections.h
> > @@ -6,4 +6,25 @@
> >
> > extern char _exiprom[];
> >
> > +extern char __idmap_text_start[];
> > +extern char __idmap_text_end[];
> > +extern char __entry_text_start[];
> > +extern char __entry_text_end[];
> > +extern char __hyp_idmap_text_start[];
> > +extern char __hyp_idmap_text_end[];
> > +
> > +static inline bool in_entry_text(unsigned long addr)
> > +{
> > + return memory_contains(__entry_text_start, __entry_text_end,
> > + (void *)addr, 1);
> > +}
> > +
> > +static inline bool in_idmap_text(unsigned long addr)
> > +{
> > + void *a = (void *)addr;
> > + return memory_contains(__idmap_text_start, __idmap_text_end, a, 1) ||
> > + memory_contains(__hyp_idmap_text_start, __hyp_idmap_text_end,
> > + a, 1);
> > +}
> > +
> > #endif /* _ASM_ARM_SECTIONS_H */
> > diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h
> > index f9a6c5fc3fd1..a00288d75ee6 100644
> > --- a/arch/arm/include/asm/traps.h
> > +++ b/arch/arm/include/asm/traps.h
> > @@ -28,18 +28,6 @@ static inline int __in_irqentry_text(unsigned long ptr)
> > ptr < (unsigned long)&__irqentry_text_end;
> > }
> >
> > -static inline int in_exception_text(unsigned long ptr)
> > -{
> > - extern char __exception_text_start[];
> > - extern char __exception_text_end[];
> > - int in;
> > -
> > - in = ptr >= (unsigned long)&__exception_text_start &&
> > - ptr < (unsigned long)&__exception_text_end;
> > -
> > - return in ? : __in_irqentry_text(ptr);
> > -}
> > -
> > extern void __init early_trap_init(void *);
> > extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame);
> > extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs);
> > diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
> > index 10ad44f3886a..b8ab97dfdd17 100644
> > --- a/arch/arm/kernel/entry-armv.S
> > +++ b/arch/arm/kernel/entry-armv.S
> > @@ -82,11 +82,7 @@
> > #endif
> > .endm
> >
> > -#ifdef CONFIG_KPROBES
> > - .section .kprobes.text,"ax",%progbits
> > -#else
> > - .text
> > -#endif
> > + .section .entry.text,"ax",%progbits
> >
> > /*
> > * Invalid mode handlers
> > diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
> > index e655dcd0a933..3c4f88701f22 100644
> > --- a/arch/arm/kernel/entry-common.S
> > +++ b/arch/arm/kernel/entry-common.S
> > @@ -37,6 +37,7 @@ saved_pc .req lr
> > #define TRACE(x...)
> > #endif
> >
> > + .section .entry.text,"ax",%progbits
> > .align 5
> > #if !(IS_ENABLED(CONFIG_TRACE_IRQFLAGS) || IS_ENABLED(CONFIG_CONTEXT_TRACKING))
> > /*
> > diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
> > index 65228bf4c6df..a56e7c856ab5 100644
> > --- a/arch/arm/kernel/stacktrace.c
> > +++ b/arch/arm/kernel/stacktrace.c
> > @@ -3,6 +3,7 @@
> > #include <linux/sched/debug.h>
> > #include <linux/stacktrace.h>
> >
> > +#include <asm/sections.h>
> > #include <asm/stacktrace.h>
> > #include <asm/traps.h>
> >
> > @@ -63,7 +64,6 @@ EXPORT_SYMBOL(walk_stackframe);
> > #ifdef CONFIG_STACKTRACE
> > struct stack_trace_data {
> > struct stack_trace *trace;
> > - unsigned long last_pc;
> > unsigned int no_sched_functions;
> > unsigned int skip;
> > };
> > @@ -87,16 +87,7 @@ static int save_trace(struct stackframe *frame, void *d)
> > if (trace->nr_entries >= trace->max_entries)
> > return 1;
> >
> > - /*
> > - * in_exception_text() is designed to test if the PC is one of
> > - * the functions which has an exception stack above it, but
> > - * unfortunately what is in frame->pc is the return LR value,
> > - * not the saved PC value. So, we need to track the previous
> > - * frame PC value when doing this.
> > - */
> > - addr = data->last_pc;
> > - data->last_pc = frame->pc;
> > - if (!in_exception_text(addr))
> > + if (!in_entry_text(frame->pc))
> > return 0;
> >
> > regs = (struct pt_regs *)frame->sp;
> > @@ -114,7 +105,6 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
> > struct stackframe frame;
> >
> > data.trace = trace;
> > - data.last_pc = ULONG_MAX;
> > data.skip = trace->skip;
> > data.no_sched_functions = nosched;
> >
> > diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
> > index 5de2bc46153f..95978073db53 100644
> > --- a/arch/arm/kernel/traps.c
> > +++ b/arch/arm/kernel/traps.c
> > @@ -73,7 +73,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long
> > printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
> > #endif
> >
> > - if (in_exception_text(where))
> > + if (in_entry_text(from))
> > dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
> > }
> >
> > @@ -434,7 +434,7 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
> > return fn ? fn(regs, instr) : 1;
> > }
> >
> > -asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
> > +asmlinkage void do_undefinstr(struct pt_regs *regs)
> > {
> > unsigned int instr;
> > siginfo_t info;
> > diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
> > index ee53f6518872..84a1ae3ce46e 100644
> > --- a/arch/arm/kernel/vmlinux.lds.S
> > +++ b/arch/arm/kernel/vmlinux.lds.S
> > @@ -105,9 +105,9 @@ SECTIONS
> > .text : { /* Real text segment */
> > _stext = .; /* Text and read-only data */
> > IDMAP_TEXT
> > - __exception_text_start = .;
> > - *(.exception.text)
> > - __exception_text_end = .;
> > + __entry_text_start = .;
> > + *(.entry.text)
> > + __entry_text_end = .;
> > IRQENTRY_TEXT
> > SOFTIRQENTRY_TEXT
> > TEXT_TEXT
> > diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
> > index 42f585379e19..b75eada23d0a 100644
> > --- a/arch/arm/mm/fault.c
> > +++ b/arch/arm/mm/fault.c
> > @@ -21,7 +21,6 @@
> > #include <linux/highmem.h>
> > #include <linux/perf_event.h>
> >
> > -#include <asm/exception.h>
> > #include <asm/pgtable.h>
> > #include <asm/system_misc.h>
> > #include <asm/system_info.h>
> > @@ -545,7 +544,7 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *)
> > /*
> > * Dispatch a data abort to the relevant handler.
> > */
> > -asmlinkage void __exception
> > +asmlinkage void
> > do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
> > {
> > const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
> > @@ -578,7 +577,7 @@ hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *
> > ifsr_info[nr].name = name;
> > }
> >
> > -asmlinkage void __exception
> > +asmlinkage void
> > do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
> > {
> > const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
> > diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c
> > index 52d1cd14fda4..e90cc8a08186 100644
> > --- a/arch/arm/probes/kprobes/core.c
> > +++ b/arch/arm/probes/kprobes/core.c
> > @@ -32,6 +32,7 @@
> > #include <linux/percpu.h>
> > #include <linux/bug.h>
> > #include <asm/patch.h>
> > +#include <asm/sections.h>
> >
> > #include "../decode-arm.h"
> > #include "../decode-thumb.h"
> > @@ -64,9 +65,6 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
> > int is;
> > const struct decode_checker **checkers;
> >
> > - if (in_exception_text(addr))
> > - return -EINVAL;
> > -
> > #ifdef CONFIG_THUMB2_KERNEL
> > thumb = true;
> > addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
> > @@ -680,3 +678,13 @@ int __init arch_init_kprobes()
> > #endif
> > return 0;
> > }
> > +
> > +bool arch_within_kprobe_blacklist(unsigned long addr)
> > +{
> > + void *a = (void *)addr;
> > +
> > + return __in_irqentry_text(addr) ||
> > + in_entry_text(addr) ||
> > + in_idmap_text(addr) ||
> > + memory_contains(__kprobes_text_start, __kprobes_text_end, a, 1);
> > +}
> > --
> > 2.7.4
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
> Hi Russel,
Hi Sa,
> Can you please tell us what is the status of this patch? It fixes the
> issue for us ([1]), but we are waiting for it to be merged.
>
> Tested-by: Sam Protsenko <semen.protsenko@linaro.org>
>
> Thanks!
>
> [1] https://bugs.linaro.org/show_bug.cgi?id=3297
It's queued for the next merge window because it's not a regression,
but rather a large patch to fix a long standing bug. Given that the
bug has been around for a long time (probably since kprobes was
originally added), I see no reason to push it for the -rc kernels,
especially given that it has taken about a month to get any kind of
feedback on this particular patch.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up
^ permalink raw reply
* [PATCH 0/3] Add DVFS support on CPU clock for Armada 37xx
From: Gregory CLEMENT @ 2017-12-22 10:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171221231341.GC7997@codeaurora.org>
Hi Stephen,
On jeu., d?c. 21 2017, Stephen Boyd <sboyd@codeaurora.org> wrote:
> On 11/30, Gregory CLEMENT wrote:
>> Hi,
>>
>> This small series is needed to use DVFS on Armada 37xx. When DVFS is
>> enabled the CPU clock setting is done using an other set of registers
>> from the North Bridge Power Management block.
>>
>> The series adds the possibility to modify the CPU frequency using the
>> associate load level matching the target frequency. However
>> configuring the frequencies for each load is done by the cpufreq
>> driver submitted in a separate series.
>>
>> Obviously having both series (cpufreq and clk) is needed to support
>> DVFS on Armada 37xx, but there is no dependencies between the series
>> (for building or at runtime).
>>
>
> Are you relying on the clk API returning an error to detect if
> DVFS is present or not? Just curious why that part of the code
> was there.
The cpufreq framework rely on the clk framwork to setup the clk
frequency of the CPU. For this hardware when DVFS is enabled the we
don't directly control the frequency of the CPUs but the "load
level". And it is during the initialization that we associate CPU
frequency to a load level.
The clk part is there to setup this load level for the hardware but by
still using the frequency as an entry point as it was what is expected
by the kernel. So cpufreq will ask a frequency depending of its policy,
then the clk driver will setup the load level matching this frequency.
And to answer your specific question we don't rely on the clk API
returning an error to detect if DVFS is present or not. For this we
directly read the DVFS bit exposed through the syscon.
Gregory
>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH] Wind down ARM/TANGO port
From: Marc Gonzalez @ 2017-12-22 10:03 UTC (permalink / raw)
To: linux-arm-kernel
This is the end. Update port status. Change contact address. Add Mans.
Signed-off-by: Marc Gonzalez <marc_gonzalez@sigmadesigns.com>
---
Sigma is being sold. It is unlikely the STB unit will survive.
---
MAINTAINERS | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index a6e86e20761e..96225e1ae5e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1956,9 +1956,10 @@ N: stm32
F: drivers/clocksource/armv7m_systick.c
ARM/TANGO ARCHITECTURE
-M: Marc Gonzalez <marc_gonzalez@sigmadesigns.com>
+M: Marc Gonzalez <marc.w.gonzalez@free.fr>
+M: Mans Rullgard <mans@mansr.com>
L: linux-arm-kernel at lists.infradead.org
-S: Maintained
+S: Odd Fixes
N: tango
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
--
2.15.0
^ permalink raw reply related
* [PATCH 1/1] ARM: imx_v6_v7_defconfig: enable CONFIG_CPU_FREQ_STAT
From: Dong Aisheng @ 2017-12-22 10:05 UTC (permalink / raw)
To: linux-arm-kernel
It is very useful for user to retrieve cpufreq transtion statistics
and worth to be default enabled.
Generated by make ARCH=arm savedefconfig.
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
arch/arm/configs/imx_v6_v7_defconfig | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index 0d44949..e58be5d 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -48,13 +48,12 @@ CONFIG_PCI_IMX6=y
CONFIG_SMP=y
CONFIG_ARM_PSCI=y
CONFIG_PREEMPT_VOLUNTARY=y
-CONFIG_AEABI=y
CONFIG_HIGHMEM=y
-CONFIG_CMA=y
CONFIG_FORCE_MAX_ZONEORDER=14
CONFIG_CMDLINE="noinitrd console=ttymxc0,115200"
CONFIG_KEXEC=y
CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
@@ -81,7 +80,6 @@ CONFIG_CAN_FLEXCAN=y
CONFIG_BT=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_LL=y
CONFIG_CFG80211=y
CONFIG_CFG80211_WEXT=y
CONFIG_MAC80211=y
@@ -90,7 +88,6 @@ CONFIG_RFKILL_INPUT=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
-CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=64
CONFIG_IMX_WEIM=y
CONFIG_CONNECTOR=y
@@ -167,9 +164,9 @@ CONFIG_MOUSE_PS2_ELANTECH=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ADS7846=y
CONFIG_TOUCHSCREEN_EGALAX=y
+CONFIG_TOUCHSCREEN_MAX11801=y
CONFIG_TOUCHSCREEN_IMX6UL_TSC=y
CONFIG_TOUCHSCREEN_EDT_FT5X06=y
-CONFIG_TOUCHSCREEN_MAX11801=y
CONFIG_TOUCHSCREEN_MC13783=y
CONFIG_TOUCHSCREEN_TSC2004=y
CONFIG_TOUCHSCREEN_TSC2007=y
@@ -178,7 +175,6 @@ CONFIG_TOUCHSCREEN_SX8654=y
CONFIG_TOUCHSCREEN_COLIBRI_VF50=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_MMA8450=y
-CONFIG_HID_MULTITOUCH=y
CONFIG_SERIO_SERPORT=m
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_IMX=y
@@ -228,13 +224,13 @@ CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_MC13783=y
CONFIG_REGULATOR_MC13892=y
CONFIG_REGULATOR_PFUZE100=y
+CONFIG_RC_CORE=y
+CONFIG_RC_DEVICES=y
+CONFIG_IR_GPIO_CIR=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
-CONFIG_RC_CORE=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_RC_DEVICES=y
-CONFIG_IR_GPIO_CIR=y
CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=m
CONFIG_V4L_PLATFORM_DRIVERS=y
@@ -245,7 +241,6 @@ CONFIG_VIDEO_CODA=m
# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
CONFIG_VIDEO_ADV7180=m
CONFIG_VIDEO_OV5640=m
-CONFIG_SOC_CAMERA_OV2640=y
CONFIG_IMX_IPUV3_CORE=y
CONFIG_DRM=y
CONFIG_DRM_PANEL_SIMPLE=y
@@ -283,6 +278,7 @@ CONFIG_SND_SOC_CS42XX8_I2C=y
CONFIG_SND_SOC_TLV320AIC3X=y
CONFIG_SND_SOC_WM8960=y
CONFIG_SND_SIMPLE_CARD=y
+CONFIG_HID_MULTITOUCH=y
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_MXC=y
--
2.7.4
^ permalink raw reply related
* [PATCH v2] ARM: dts: sunxi: Add sid for a83t
From: Emmanuel Vadot @ 2017-12-22 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222083508.dfcp6egfvxykmogg@flea.lan>
On Fri, 22 Dec 2017 09:35:08 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> On Thu, Dec 21, 2017 at 07:09:03PM +0100, Emmanuel Vadot wrote:
> >
> > Hi Maxime,
> >
> > On Thu, 21 Dec 2017 16:26:30 +0100
> > Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> >
> > > Hi,
> > >
> > > On Thu, Dec 21, 2017 at 09:19:24AM -0600, Kyle Evans wrote:
> > > > On Thu, Dec 21, 2017 at 8:55 AM, Maxime Ripard
> > > > <maxime.ripard@free-electrons.com> wrote:
> > > > > Hi Kyle,
> > > > >
> > > > > On Tue, Dec 19, 2017 at 03:05:23PM -0600, kevans91 at ksu.edu wrote:
> > > > >> Allwinner a83t has a 1 KB sid block with efuse for security rootkey and
> > > > >> thermal calibration data, add node to describe it.
> > > > >>
> > > > >> a83t-sid is not currently supported by nvmem/sunxi-sid, but it is
> > > > >> supported in an external driver for FreeBSD.
> > > > >>
> > > > >> Signed-off-by: Kyle Evans <kevans91@ksu.edu>
> > > > >
> > > > > The patch looks fine in itself, but we've had a number of issues with
> > > > > the register layout (and access patterns) in the past, so I'd rather
> > > > > have something that works in Linux too if possible.
> > > >
> > > > I have a patch that I think should make it work fine on Linux [1], but
> > > > I'm afraid I have little to no capability to test it myself and so I
> > > > did not add it as well.
> > > >
> > > > I do know that the rootkey is offset 0x200 into the given space [2],
> > > > as is the case with the H3, and that the readout quirk is not needed.
> > > > I wasn't 100% sure that the a83t has 2Kbit worth of efuse space as the
> > > > H3, but I do know that thermal data can be found at 0x34 and 0x38 in
> > > > this space.
> > >
> > > Then maybe we should leave it aside until someone takes some time on
> > > the A83t.
> >
> > Take some time on the Linux driver and do not apply this patch for
> > now you mean ?
>
> Yep.
>
> Maxime
Since linux doesn't have the compatible in it's driver what would
be the harm to add the node in the DTS ? If a quirks is needed because
some region is weird this would go in the driver right ? I don't see a
technical problem for adding this node right now.
If Kyle confirm the lenght of the region and that no quirk is needed
will it be enough ?
Cheers,
--
Emmanuel Vadot <manu@bidouilliste.com>
^ permalink raw reply
* [PATCH v2 5/8] arm64: dts: actions: Add S700 and CubieBoard7
From: Andreas Färber @ 2017-12-22 10:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171113233427.5386-6-afaerber@suse.de>
Am 14.11.2017 um 00:34 schrieb Andreas F?rber:
> Add Device Trees for S700 SoC and Cubietech CubieBoard7.
>
> Signed-off-by: Andreas F?rber <afaerber@suse.de>
> ---
> v1 -> v2: Unchanged
>
> arch/arm64/boot/dts/actions/Makefile | 2 +
> arch/arm64/boot/dts/actions/s700-cubieboard7.dts | 47 +++++++
> arch/arm64/boot/dts/actions/s700.dtsi | 164 +++++++++++++++++++++++
> 3 files changed, 213 insertions(+)
> create mode 100644 arch/arm64/boot/dts/actions/s700-cubieboard7.dts
> create mode 100644 arch/arm64/boot/dts/actions/s700.dtsi
Squashed patch updated with new SPDX header style and applied:
https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-actions.git/log/?h=v4.16/dt64
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
^ permalink raw reply
* [PATCH v6 06/11] thermal: armada: Add support for Armada AP806
From: Baruch Siach @ 2017-12-22 10:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222093226.23456-7-miquel.raynal@free-electrons.com>
Hi Miqu?l,
On Fri, Dec 22, 2017 at 10:32:21AM +0100, Miquel Raynal wrote:
> From: Baruch Siach <baruch@tkos.co.il>
>
> The AP806 component is integrated in the Armada 8K and 7K lines of
> processors.
>
> The thermal sensor sample field on the status register is a signed
> value. Extend armada_get_temp() and the driver structure to handle
> signed values.
>
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>
> [<miquel.raynal@free-electrons.com>: Changes when applying over the
> previous patches, including the register names changes, also switched
> the coefficients values to s64 instead of unsigned long to deal with
> negative values and used do_div instead of the traditionnal '/']
> Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
> Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> Tested-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
[..]
> static int armada_get_temp(struct thermal_zone_device *thermal,
> - int *temp)
> + int *temperature)
> {
> struct armada_thermal_priv *priv = thermal->devdata;
> - unsigned long reg;
> - unsigned long m, b, div;
> + u32 reg, div;
> + s64 sample, b, m;
> + u64 tmp;
>
> /* Valid check */
> if (priv->data->is_valid && !priv->data->is_valid(priv)) {
> @@ -178,6 +197,11 @@ static int armada_get_temp(struct thermal_zone_device *thermal,
>
> reg = readl_relaxed(priv->status);
> reg = (reg >> priv->data->temp_shift) & priv->data->temp_mask;
> + if (priv->data->signed_sample)
> + /* The most significant bit is the sign bit */
> + sample = sign_extend32(reg, fls(priv->data->temp_mask) - 1);
> + else
> + sample = reg;
>
> /* Get formula coeficients */
> b = priv->data->coef_b;
> @@ -185,9 +209,13 @@ static int armada_get_temp(struct thermal_zone_device *thermal,
> div = priv->data->coef_div;
>
> if (priv->data->inverted)
> - *temp = ((m * reg) - b) / div;
> + tmp = (m * sample) - b;
> else
> - *temp = (b - (m * reg)) / div;
> + tmp = b - (m * sample);
> +
> + do_div(tmp, div);
> + *temperature = (int)tmp;
Nitpick: why not (untested)
#include <linux/math64.h>
if (priv->data->inverted)
*temp = div_s64((m * sample) - b, div);
else
*temp = div_s64(b - (m * sample), div);
baruch
> +
> return 0;
> }
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply
* [GIT PULL] arm: actions: dt for v4.16 #1
From: Andreas Färber @ 2017-12-22 10:21 UTC (permalink / raw)
To: linux-arm-kernel
Hi Olof and Arnd,
Here's my dt pull for Actions Semi.
This adds one new S500 based board, which I already updated to new SPDX style.
A second pull will follow after I've converted the existing boards.
Regards,
Andreas
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-actions.git tags/actions-arm-dt-for-4.16
for you to fetch changes up to 271a70da383cf69f32742d9e2d01a7b16d04d60c:
arm: dts: owl-s500: Add Sparky (2017-12-22 10:53:52 +0100)
----------------------------------------------------------------
Actions Semi arm based SoC DT for v4.16
This adds a DT for the Allo.com Sparky SBC.
----------------------------------------------------------------
Andreas F?rber (3):
dt-bindings: Add vendor prefix for Allo.com
dt-bindings: arm: actions: Add Sparky
arm: dts: owl-s500: Add Sparky
Documentation/devicetree/bindings/arm/actions.txt | 1 +
.../devicetree/bindings/vendor-prefixes.txt | 1 +
arch/arm/boot/dts/Makefile | 3 +-
arch/arm/boot/dts/owl-s500-sparky.dts | 43 ++++++++++++++++++++++
4 files changed, 47 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/boot/dts/owl-s500-sparky.dts
^ permalink raw reply
* [GIT PULL] arm: actions: dt64 for v4.16 #1
From: Andreas Färber @ 2017-12-22 10:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171222102107.5661-1-afaerber@suse.de>
Hi Olof and Arnd,
Here's my dt64 pull for Actions Semi.
This adds S700 and the CubieBoard7. Timer binding will go via timer tree.
A second pull will follow after I've converted S900 to new SPDX style.
Regards,
Andreas
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:
Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-actions.git tags/actions-arm64-dt-for-4.16
for you to fetch changes up to 0bea2a65387961efcb59794a9fb7f9a63c6eb722:
arm64: dts: actions: Add S700 and CubieBoard7 (2017-12-22 10:52:54 +0100)
----------------------------------------------------------------
Actions Semi arm64 based SoC DT for v4.16
This adds S700 SoC and CubieBoard7.
----------------------------------------------------------------
Andreas F?rber (3):
dt-bindings: arm: actions: Add S700 and CubieBoard7
dt-bindings: power: Add Actions Semi S700 SPS
arm64: dts: actions: Add S700 and CubieBoard7
Documentation/devicetree/bindings/arm/actions.txt | 15 ++
.../devicetree/bindings/power/actions,owl-sps.txt | 2 +
arch/arm64/boot/dts/actions/Makefile | 2 +
arch/arm64/boot/dts/actions/s700-cubieboard7.dts | 46 ++++++
arch/arm64/boot/dts/actions/s700.dtsi | 169 +++++++++++++++++++++
include/dt-bindings/power/owl-s700-powergate.h | 19 +++
6 files changed, 253 insertions(+)
create mode 100644 arch/arm64/boot/dts/actions/s700-cubieboard7.dts
create mode 100644 arch/arm64/boot/dts/actions/s700.dtsi
create mode 100644 include/dt-bindings/power/owl-s700-powergate.h
^ 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