* [PATCH 3/4] dt-bindings: rtc: update stm32-rtc documentation for st,syscfg property
From: Alexandre Torgue @ 2018-05-04 7:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180503205351.GE10960@piout.net>
Hi Alexandre,
On 05/03/2018 10:53 PM, Alexandre Belloni wrote:
> Amelie,
>
> On 26/04/2018 21:58:03-0500, Rob Herring wrote:
>> On Thu, Apr 19, 2018 at 03:21:42PM +0200, Amelie Delaunay wrote:
>>> RTC driver should not be aware of the PWR registers offset and bits
>>> position. Furthermore, we can imagine that Disable Backup Protection (DBP)
>>> relative register and bit mask could change depending on the SoC. So this
>>> patch moves st,syscfg property from single pwrcfg phandle to pwrcfg
>>> phandle/offset/mask triplet.
>>>
>>> Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
>>> ---
>>> Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt | 10 ++++++----
>>> 1 file changed, 6 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>>> index a66692a..00f8b5d 100644
>>> --- a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>>> +++ b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>>> @@ -14,8 +14,10 @@ Required properties:
>>> It is required only on stm32h7.
>>> - interrupt-parent: phandle for the interrupt controller.
>>> - interrupts: rtc alarm interrupt.
>>> -- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
>>> - (RTC registers) write protection.
>>> +- st,syscfg: phandle/offset/mask triplet. The phandle to pwrcfg used to
>>> + access control register at offset, and change the dbp (Disable Backup
>>> + Protection) bit represented by the mask, mandatory to disable/enable backup
>>> + domain (RTC registers) write protection.
>>
>> It's fine to add this, but you are breaking compatibility in the driver
>> with existing DTBs by requiring these new fields.
>>
>
> I'm fine with that change but I would like confirmation that this has
> been well thought. Maybe Maxime or Alexandre could give their ack.
>
It's a good thing to remove PWR registers information from RTC driver.
My only concern was the compatibility with old DT but we can accept it.
Indeed, Kernel will continue to boot fine, only RTC will not probe if we
use old DT.
Acked-by: Alexandre TORGUE <alexandre.torgue@st.com>
Regards
alex
^ permalink raw reply
* [PATCH v1] drm/kms/mode: added a new helper for calculating videomode from crtc's display mode
From: Satendra Singh Thakur @ 2018-05-04 7:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180503103639.GH1027@e110455-lin.cambridge.arm.com>
1.
-Added a new helper drm_display_mode_crtc_to_videomode
-This helper calculates mode parameters like
--horizontal front_porch, back_porch, sync length
--vertical front_porch, back_porch, sync length
-using crtc fields of struct drm_display_mode
-It uses following fields of crtc mode
--horizontal sync start/end, active and total length
--vertical sync start/end, active and total length
2.
-Most of the driver use user-supplied mode for calculating videomode
-However, few drivers use HW (crtc) mode for calculating videomode
-This helper will be useful for such drivers
3.
-Currently following drivers will be using this new helper
-arm hdlcd
-atmel hlcdc
-exynos 5433 decon
-exynos7 decon
-exynos fimd
4.
-This change reduces several redundant lines of code for many drivers
Reviewed-by: Jani Nikula <jani.nikula@linux.intel.com>
Acked-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Satendra Singh Thakur <satendra.t@samsung.com>
Acked-by: Madhur Verma <madhur.verma@samsung.com>
Cc: Hemanshu Srivastava <hemanshu.s@samsung.com>
---
v1: Fixed review comments by Mr Jani
Initialized with 0 the struct videomode so that stack garbage won't come
Added Reviewed-by and Acked-by fields
drivers/gpu/drm/arm/hdlcd_crtc.c | 10 ++--------
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 9 ++-------
drivers/gpu/drm/drm_modes.c | 19 +++++++++++++++++++
drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 22 ++++++++++------------
drivers/gpu/drm/exynos/exynos7_drm_decon.c | 23 ++++++++++-------------
drivers/gpu/drm/exynos/exynos_drm_fimd.c | 22 +++++++++-------------
include/drm/drm_modes.h | 2 ++
7 files changed, 54 insertions(+), 53 deletions(-)
diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c
index cf5cbd6..5cec264 100644
--- a/drivers/gpu/drm/arm/hdlcd_crtc.c
+++ b/drivers/gpu/drm/arm/hdlcd_crtc.c
@@ -127,16 +127,10 @@ static void hdlcd_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
struct drm_display_mode *m = &crtc->state->adjusted_mode;
- struct videomode vm;
+ struct videomode vm = {};
unsigned int polarities, err;
- vm.vfront_porch = m->crtc_vsync_start - m->crtc_vdisplay;
- vm.vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
- vm.vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
- vm.hfront_porch = m->crtc_hsync_start - m->crtc_hdisplay;
- vm.hback_porch = m->crtc_htotal - m->crtc_hsync_end;
- vm.hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
-
+ drm_display_mode_crtc_to_videomode(m, &vm);
polarities = HDLCD_POLARITY_DATAEN | HDLCD_POLARITY_DATA;
if (m->flags & DRM_MODE_FLAG_PHSYNC)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index d732810..fb298b8 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -76,17 +76,12 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
struct drm_display_mode *adj = &c->state->adjusted_mode;
struct atmel_hlcdc_crtc_state *state;
unsigned long mode_rate;
- struct videomode vm;
+ struct videomode vm = {};
unsigned long prate;
unsigned int cfg;
int div;
- vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
- vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
- vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
- vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
- vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
- vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
+ drm_display_mode_crtc_to_videomode(adj, &vm);
regmap_write(regmap, ATMEL_HLCDC_CFG(1),
(vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index e82b61e..328f771 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -654,6 +654,25 @@ void drm_display_mode_to_videomode(const struct drm_display_mode *dmode,
vm->flags |= DISPLAY_FLAGS_DOUBLECLK;
}
EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode);
+/**
+ * drm_display_mode_crtc_to_videomode - fill in @vm using crtc fields of @dmode
+ * @dmode: drm_display_mode structure to use as source
+ * @vm: videomode structure to use as destination
+ *
+ * Fills out @vm using the crtc display mode specified in @dmode.
+ */
+void drm_display_mode_crtc_to_videomode(const struct drm_display_mode *dmode,
+ struct videomode *vm)
+{
+ vm->hfront_porch = dmode->crtc_hsync_start - dmode->crtc_hdisplay;
+ vm->hsync_len = dmode->crtc_hsync_end - dmode->crtc_hsync_start;
+ vm->hback_porch = dmode->crtc_htotal - dmode->crtc_hsync_end;
+ vm->vfront_porch = dmode->crtc_vsync_start - dmode->crtc_vdisplay;
+ vm->vsync_len = dmode->crtc_vsync_end - dmode->crtc_vsync_start;
+ vm->vback_porch = dmode->crtc_vtotal - dmode->crtc_vsync_end;
+
+}
+EXPORT_SYMBOL_GPL(drm_display_mode_crtc_to_videomode);
/**
* drm_bus_flags_from_videomode - extract information about pixelclk and
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
index 1c330f2..2535ebb 100644
--- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
@@ -20,6 +20,7 @@
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <video/videomode.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
@@ -225,26 +226,23 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
writel(val, ctx->addr + DECON_VIDTCON2);
if (!crtc->i80_mode) {
- int vbp = m->crtc_vtotal - m->crtc_vsync_end;
- int vfp = m->crtc_vsync_start - m->crtc_vdisplay;
+ struct videomode vm = {};
+ drm_display_mode_crtc_to_videomode(m, &vm);
if (interlaced)
- vbp = vbp / 2 - 1;
- val = VIDTCON00_VBPD_F(vbp - 1) | VIDTCON00_VFPD_F(vfp - 1);
+ vm.vback_porch = (vm.vback_porch >> 1) - 1;
+ val = VIDTCON00_VBPD_F(vm.vback_porch - 1) |
+ VIDTCON00_VFPD_F(vm.vfront_porch - 1);
writel(val, ctx->addr + DECON_VIDTCON00);
- val = VIDTCON01_VSPW_F(
- m->crtc_vsync_end - m->crtc_vsync_start - 1);
+ val = VIDTCON01_VSPW_F(vm.vsync_len - 1);
writel(val, ctx->addr + DECON_VIDTCON01);
- val = VIDTCON10_HBPD_F(
- m->crtc_htotal - m->crtc_hsync_end - 1) |
- VIDTCON10_HFPD_F(
- m->crtc_hsync_start - m->crtc_hdisplay - 1);
+ val = VIDTCON10_HBPD_F(vm.hback_porch - 1) |
+ VIDTCON10_HFPD_F(vm.hfront_porch - 1);
writel(val, ctx->addr + DECON_VIDTCON10);
- val = VIDTCON11_HSPW_F(
- m->crtc_hsync_end - m->crtc_hsync_start - 1);
+ val = VIDTCON11_HSPW_F(vm.hsync_len - 1);
writel(val, ctx->addr + DECON_VIDTCON11);
}
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 3931d5e..5fdd093 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -25,6 +25,7 @@
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
+#include <video/videomode.h>
#include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h"
@@ -168,28 +169,24 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
return;
if (!ctx->i80_if) {
- int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
+ struct videomode vm = {};
+
+ drm_display_mode_crtc_to_videomode(mode, &vm);
/* setup vertical timing values. */
- vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
- vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
- vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
- val = VIDTCON0_VBPD(vbpd - 1) | VIDTCON0_VFPD(vfpd - 1);
+ val = VIDTCON0_VBPD(vm.vback_porch - 1) |
+ VIDTCON0_VFPD(vm.vfront_porch - 1);
writel(val, ctx->regs + VIDTCON0);
- val = VIDTCON1_VSPW(vsync_len - 1);
+ val = VIDTCON1_VSPW(vm.vsync_len - 1);
writel(val, ctx->regs + VIDTCON1);
/* setup horizontal timing values. */
- hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
- hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
- hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
-
- /* setup horizontal timing values. */
- val = VIDTCON2_HBPD(hbpd - 1) | VIDTCON2_HFPD(hfpd - 1);
+ val = VIDTCON2_HBPD(vm.hback_porch - 1) |
+ VIDTCON2_HFPD(vm.hfront_porch - 1);
writel(val, ctx->regs + VIDTCON2);
- val = VIDTCON3_HSPW(hsync_len - 1);
+ val = VIDTCON3_HSPW(vm.hsync_len - 1);
writel(val, ctx->regs + VIDTCON3);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index d42ae2b..c70b9ec 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -25,6 +25,7 @@
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
+#include <video/videomode.h>
#include <video/samsung_fimd.h>
#include <drm/exynos_drm.h>
@@ -463,7 +464,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
return;
}
} else {
- int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
+ struct videomode vm = {};
u32 vidcon1;
/* setup polarity values */
@@ -474,24 +475,19 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
vidcon1 |= VIDCON1_INV_HSYNC;
writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
+ drm_display_mode_crtc_to_videomode(mode, &vm);
/* setup vertical timing values. */
- vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
- vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
- vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
- val = VIDTCON0_VBPD(vbpd - 1) |
- VIDTCON0_VFPD(vfpd - 1) |
- VIDTCON0_VSPW(vsync_len - 1);
+ val = VIDTCON0_VBPD(vm.vback_porch - 1) |
+ VIDTCON0_VFPD(vm.vfront_porch - 1) |
+ VIDTCON0_VSPW(vm.vsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
/* setup horizontal timing values. */
- hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
- hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
- hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
- val = VIDTCON1_HBPD(hbpd - 1) |
- VIDTCON1_HFPD(hfpd - 1) |
- VIDTCON1_HSPW(hsync_len - 1);
+ val = VIDTCON1_HBPD(vm.hback_porch - 1) |
+ VIDTCON1_HFPD(vm.hfront_porch - 1) |
+ VIDTCON1_HSPW(vm.hsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
}
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index 0d310be..9ac764b 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -473,6 +473,8 @@ void drm_display_mode_from_videomode(const struct videomode *vm,
struct drm_display_mode *dmode);
void drm_display_mode_to_videomode(const struct drm_display_mode *dmode,
struct videomode *vm);
+void drm_display_mode_crtc_to_videomode(const struct drm_display_mode *dmode,
+ struct videomode *vm);
void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags);
int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode, u32 *bus_flags,
--
2.7.4
^ permalink raw reply related
* [PATCH] arm64: dts: ls208xa-rdb: Pass unit name to SPI flash node
From: Shawn Guo @ 2018-05-04 7:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1524601644-23607-1-git-send-email-festevam@gmail.com>
On Tue, Apr 24, 2018 at 05:27:24PM -0300, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> Pass unit name to SPI flash node to match its 'reg' value and
> also avoid the following DTC warnings:
>
> arch/arm64/boot/dts/freescale/fsl-ls2080a-rdb.dtb: Warning (unit_address_vs_reg): /soc/dspi at 2100000/n25q512a: node has a reg or ranges property, but no unit name
> arch/arm64/boot/dts/freescale/fsl-ls2088a-rdb.dtb: Warning (unit_address_vs_reg): /soc/dspi at 2100000/n25q512a: node has a reg or ranges property, but no unit name
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Applied, thanks.
^ permalink raw reply
* [PATCH] ARM: dts: imx6q-pistachio: Use 'uart-has-rtscts' property
From: Shawn Guo @ 2018-05-04 7:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1524601364-23342-1-git-send-email-festevam@gmail.com>
On Tue, Apr 24, 2018 at 05:22:44PM -0300, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> The 'fsl,uart-has-rtscts' property is deprecated and it is recommended
> to use the generic 'uart-has-rtscts' property instead.
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Applied, thanks.
^ permalink raw reply
* [PATCH] ARM: dts: imx7s: Pass the 'fsl,sec-era' property
From: Shawn Guo @ 2018-05-04 7:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1524601519-23502-1-git-send-email-festevam@gmail.com>
On Tue, Apr 24, 2018 at 05:25:19PM -0300, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> Currently the following error is seen from the CAAM driver:
>
> caam 30900000.caam: device ID = 0x0a16030000000000 (Era -524)
>
> Pass the 'fsl,sec-era' property to properly describe the
> era information.
>
> This error happens because the 'fsl,sec-era' is not passed via
> device tree.
>
> The era information is used in various places inside drivers/crypto/caam,
> so pass the correct version via device tree.
The property is documented as optional in the bindings. Missing the
property shouldn't cause any fatal error, I guess.
Shawn
>
> Fixes: 0eeabcad7da5 ("ARM: dts: imx7s: add CAAM device node")
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
> ---
> I have sent a patch for 4.18 that allows retrieving the era information
> from the CAAM registers in run-time:
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/crypto/caam/ctrl.c?h=next-20180424&id=654f2b937b389295581bcb4aa26011a63db7bc8f
>
> This will only be applied in 4.18 though.
>
> In order to avoid i.MX7 report the incorrect era version in 4.17,
> let's pass this property.
>
> arch/arm/boot/dts/imx7s.dtsi | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
> index a6fe324..b416d2b 100644
> --- a/arch/arm/boot/dts/imx7s.dtsi
> +++ b/arch/arm/boot/dts/imx7s.dtsi
> @@ -884,6 +884,7 @@
>
> crypto: caam at 30900000 {
> compatible = "fsl,sec-v4.0";
> + fsl,sec-era = <8>;
> #address-cells = <1>;
> #size-cells = <1>;
> reg = <0x30900000 0x40000>;
> --
> 2.7.4
>
^ permalink raw reply
* [alsa-devel] [PATCH v7 16/24] ASoC: qdsp6: q6asm: Add support to audio stream apis
From: Rohit Kumar @ 2018-05-04 7:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180501120820.11016-17-srinivas.kandagatla@linaro.org>
On 5/1/2018 5:38 PM, Srinivas Kandagatla wrote:
> +static int __q6asm_run(struct audio_client *ac, uint32_t flags,
> + uint32_t msw_ts, uint32_t lsw_ts, bool wait)
> +{
> + struct asm_session_cmd_run_v2 *run;
> + struct apr_pkt *pkt;
> + int pkt_size, rc;
> + void *p;
> +
> + pkt_size = APR_HDR_SIZE + sizeof(*run);
> + p = kzalloc(pkt_size, GFP_KERNEL);
Should be GFP_ATOMIC as this API is also called from interrupt context
> + if (!p)
> + return -ENOMEM;
> +
[..]
> +int q6asm_read(struct audio_client *ac)
> +{
> + struct asm_data_cmd_read_v2 *read;
> + struct audio_port_data *port;
> + struct audio_buffer *ab;
> + struct apr_pkt *pkt;
> + int pkt_size;
> + int rc = 0;
> + void *p;
> +
> + if (!(ac->io_mode & ASM_SYNC_IO_MODE))
> + return 0;
> +
> + pkt_size = APR_HDR_SIZE + sizeof(*read);
> + p = kzalloc(pkt_size, GFP_KERNEL);
same here. GFP_ATOMIC
> + if (!p)
> + return -ENOMEM;
[..]
> +int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
> + uint32_t lsw_ts, uint32_t flags)
> +{
> + struct asm_data_cmd_write_v2 *write;
> + struct audio_port_data *port;
> + struct audio_buffer *ab;
> + struct apr_pkt *pkt;
> + int pkt_size;
> + int rc = 0;
> + void *p;
> +
> + pkt_size = APR_HDR_SIZE + sizeof(*write);
> + p = kzalloc(pkt_size, GFP_KERNEL);
GFP_ATOMIC
> + if (!p)
> + return -ENOMEM;
>
Thanks,
Rohit
--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v10 2/2] media: V3s: Add support for Allwinner CSI.
From: Yong Deng @ 2018-05-04 7:09 UTC (permalink / raw)
To: linux-arm-kernel
Allwinner V3s SoC features two CSI module. CSI0 is used for MIPI CSI-2
interface and CSI1 is used for parallel interface. This is not
documented in datasheet but by test 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.
Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Maxime Ripard <maxime.ripard@bootlin.com>
Tested-by: Maxime Ripard <maxime.ripard@bootlin.com>
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 | 932 +++++++++++++++++++++
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 145 ++++
.../media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 196 +++++
.../media/platform/sunxi/sun6i-csi/sun6i_video.c | 767 +++++++++++++++++
.../media/platform/sunxi/sun6i-csi/sun6i_video.h | 53 ++
10 files changed, 2116 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 0a1410d5a621..a8f9fe91dd14 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3771,6 +3771,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 e3229f7baed1..43f25c9c05e1 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -138,6 +138,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 932515df4477..69472cbfa360 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -92,3 +92,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 000000000000..018e3ec788c0
--- /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 000000000000..213cb6be9e9c
--- /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 000000000000..d03a367dad44
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -0,0 +1,932 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#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_supported(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;
+ default:
+ dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
+ mbus_code);
+ break;
+ }
+ break;
+ default:
+ dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n",
+ pixformat);
+ 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;
+ default:
+ dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
+ mbus_code);
+ break;
+ }
+ break;
+ default:
+ dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
+ 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(struct sun6i_csi_dev *sdev,
+ 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;
+ default:
+ break;
+ }
+
+ /* not support YUV420 input format yet */
+ dev_dbg(sdev->dev, "Select YUV422 as default input format of CSI.\n");
+ return CSI_INPUT_FORMAT_YUV422;
+}
+
+static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
+ 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;
+ default:
+ dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
+ break;
+ }
+
+ return CSI_FIELD_RAW_8;
+}
+
+static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
+ 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;
+ default:
+ dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
+ mbus_code);
+ break;
+ }
+ 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;
+ default:
+ dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
+ mbus_code);
+ break;
+ }
+ break;
+
+ case V4L2_PIX_FMT_YUYV:
+ return CSI_INPUT_SEQ_YUYV;
+
+ default:
+ dev_warn(sdev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n",
+ pixformat);
+ 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_LOW)
+ cfg |= CSI_IF_CFG_VREF_POL_POSITIVE;
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg |= CSI_IF_CFG_HREF_POL_POSITIVE;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ 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(sdev, csi->config.code,
+ csi->config.pixelformat);
+ cfg |= CSI_CH_CFG_INPUT_FMT(val);
+
+ val = get_csi_output_format(sdev, csi->config.pixelformat,
+ csi->config.field);
+ cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
+
+ val = get_csi_input_seq(sdev, 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:
+ dev_dbg(sdev->dev,
+ "Horizontal length should be 2 times of width for packed YUV formats!\n");
+ hor_len = width * 2;
+ break;
+ default:
+ 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 */
+ dev_dbg(sdev->dev,
+ "Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
+ config->pixelformat);
+ 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);
+
+ regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG,
+ (addr + sdev->planar_offset[0]) >> 2);
+ if (sdev->planar_offset[1] != -1)
+ regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
+ (addr + sdev->planar_offset[1]) >> 2);
+ if (sdev->planar_offset[2] != -1)
+ regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
+ (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 fwnode_handle *fwnode)
+{
+ struct media_entity *sink;
+ struct media_pad *sink_pad;
+ struct media_link *link;
+ int src_pad_index;
+ int ret;
+
+ ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE);
+ if (ret < 0) {
+ dev_err(csi->dev, "%s: no source pad in external entity %s\n",
+ __func__, entity->name);
+ return -EINVAL;
+ }
+
+ src_pad_index = ret;
+
+ sink = &csi->video.vdev.entity;
+ sink_pad = &csi->video.pad;
+
+ dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
+ entity->name, src_pad_index, sink->name, sink_pad->index);
+ ret = media_create_pad_link(entity, src_pad_index, sink,
+ sink_pad->index, 0);
+ if (ret < 0) {
+ dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
+ entity->name, src_pad_index,
+ sink->name, sink_pad->index);
+ return ret;
+ }
+
+ link = media_entity_find_link(&entity->pads[src_pad_index], sink_pad);
+ return media_entity_setup_link(link, 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, sd->fwnode);
+ 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);
+
+ ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 0);
+ if (ret) {
+ dev_err(csi->dev, "V4L2 controls handler init failed (%d)\n",
+ ret);
+ goto clean_media;
+ }
+
+ csi->v4l2_dev.mdev = &csi->media_dev;
+ csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler;
+ 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;
+}
+
+/*
+ * PHYS_OFFSET isn't available on all architectures. In order to
+ * accomodate for COMPILE_TEST, let's define it to something dumb.
+ */
+#ifndef PHYS_OFFSET
+#define PHYS_OFFSET 0
+#endif
+
+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;
+ /* The DMA bus has the memory mapped at 0 */
+ sdev->dev->dma_pfn_offset = PHYS_OFFSET >> PAGE_SHIFT;
+
+ 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");
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 000000000000..44a7d50805bc
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#ifndef __SUN6I_CSI_H__
+#define __SUN6I_CSI_H__
+
+#include <media/v4l2-ctrls.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_ctrl_handler ctrl_handler;
+ 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_supported() - 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_supported(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;
+ default:
+ WARN(1, "Unsupported pixformat: 0x%x\n", pixformat);
+ break;
+ }
+
+ 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 000000000000..00798b4d20f2
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#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 000000000000..9bd662b08236
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#include <linux/of.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.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;
+
+ if (!video->current_fmt)
+ return -ENXIO;
+
+ 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 method 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 dropped!\n");
+ }
+
+unlock:
+ video->sequence++;
+ spin_unlock(&video->dma_queue_lock);
+}
+
+static const 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_source(struct sun6i_video *video,
+ u32 which,
+ struct v4l2_pix_format *pixfmt,
+ struct sun6i_csi_format *csi_fmt)
+{
+ struct v4l2_subdev *subdev;
+ struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = which,
+ };
+ u32 pad;
+ int ret;
+
+ subdev = sun6i_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -ENXIO;
+
+ v4l2_fill_mbus_format(&format.format, pixfmt, csi_fmt->mbus_code);
+
+ pad_cfg = v4l2_subdev_alloc_pad_config(subdev);
+ if (pad_cfg == NULL)
+ return -ENOMEM;
+
+ format.pad = pad;
+ ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &format);
+ if (ret)
+ goto done;
+
+ v4l2_fill_pix_format(pixfmt, &format.format);
+
+done:
+ v4l2_subdev_free_pad_config(pad_cfg);
+ return ret;
+}
+
+static int sun6i_video_try_fmt(struct sun6i_video *video, u32 which,
+ struct v4l2_format *f,
+ struct sun6i_csi_format **current_fmt)
+{
+ struct sun6i_csi_format *csi_fmt;
+ struct v4l2_pix_format *pixfmt = &f->fmt.pix;
+ int ret;
+
+ 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;
+ }
+
+ ret = sun6i_video_try_fmt_source(video, which, pixfmt, csi_fmt);
+ if (ret)
+ return ret;
+
+ pixfmt->bytesperline = (pixfmt->width * csi_fmt->bpp) >> 3;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ if (current_fmt)
+ *current_fmt = csi_fmt;
+
+ return 0;
+}
+
+static int sun6i_video_set_fmt(struct sun6i_video *video, struct v4l2_format *f)
+{
+ struct sun6i_csi_format *current_fmt;
+ int ret;
+
+ ret = sun6i_video_try_fmt(video, V4L2_SUBDEV_FORMAT_ACTIVE, f,
+ ¤t_fmt);
+ if (ret)
+ 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, V4L2_SUBDEV_FORMAT_TRY, 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 = 0;
+
+ inp->capabilities = 0;
+ inp->std = 0;
+ if (v4l2_subdev_has_op(subdev, pad, dv_timings_cap))
+ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+
+ 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,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+};
+
+/* -----------------------------------------------------------------------------
+ * 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,
+ const struct media_pad *remote)
+{
+ 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;
+
+ pad = remote->index;
+ subdev = media_entity_to_v4l2_subdev(remote->entity);
+ 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_supported(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_supported(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;
+
+ if ((flags & MEDIA_LNK_FL_ENABLED) == 0)
+ return 0;
+
+ return sun6i_video_formats_init(video, remote);
+}
+
+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 000000000000..2301f184c986
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#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 v4 08/14] dt: qcom: Add opp and thermal to the msm8996
From: Viresh Kumar @ 2018-05-04 7:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <003001d3e373$f94db540$ebe91fc0$@codeaurora.org>
On 04-05-18, 09:48, ilialin at codeaurora.org wrote:
> Kryo has single clock per cluster. I define here a shared OPP table per cluster.
Thanks for clarifying.
--
viresh
^ permalink raw reply
* [PATCH v4 6/7] ARM: dts: Add support for emtrion emCON-MX6 series
From: Shawn Guo @ 2018-05-04 7:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <95F51F4B902CAC40AF459205F6322F01B83109E5C3@BMK019S01.emtrion.local>
On Thu, May 03, 2018 at 12:00:06PM +0200, T?rk, Jan wrote:
> > > +/ {
> > > + aliases {
> > > + mmc0 = &usdhc3;
> > > + mmc2 = &usdhc1;
> > > + mmc1 = &usdhc2;
> > > + mmc3 = &usdhc4;
> > > + boardID = &boardID;
> >
> > Why do you need this boardID alias?
> I wanted to have a generic entry point to address the board-id on all CPU-modules with their different SoC's resulting in different devicetree paths.
> Also as it now has the generic "gpio at 3a" name, there would be no other way in differencing the board Identifying GPIO-expander from an additionally
> attached one (except platform code etc.)
> With the alias every code could look up the information required over the alias path with the same piece of code.
Okay. But no uppercase please. Otherwise, you introduce the following
DTC warnings (with W=1 switch) we are trying to clean up.
Warning (alias_paths): /aliases: aliases property name must include only lowercase and '-'
> >
> > > + };
<snip>
> > > &pinctrl_nor_flash
> > > + &pinctrl_usdhc2
> > > + &pinctrl_spdif_out &pinctrl_spdif_in
> > > + &pinctrl_cpi1 &pinctrl_cpi2
> > > + >;
> >
> > Again, please do not put consumer device specific pins in hog group.
> > Also, it's confusing to have the same pinctrl in both hog and consumer device
> > node, like pinctrl_nor_flash and pinctrl_usdhc2 here.
>
> About pinctrl_nor_flash I fully agree, that's a mistake.
> pinctrl_usdhc2 is not connected on the avari, and therefore not enabled.
> As told before it is added to the Hog group to force the default pinmuxing without enabling the hardware itself.
If you want to do such default pinmuxing, please do it in your firmware.
We generally do not want to use hog group too much, because that makes
it difficult to find out which client devices consume which pins.
>
> >
> > > +};
<snip>
> > > +&i2c1 {
> > > + clock-frequency = <100000>;
> > > + pinctrl-names = "default";
> > > + pinctrl-0 = <&pinctrl_i2c1>;
> > > + status = "okay";
> > > +
> > > + rtc: rtc at 68 {
> >
> > Is the label actually used? If yes, I would suggest a more specific name like
> > ds1307.
> Really? Why should it be a generic name for "gpio" and "pmic" but not a generic name for an "rtc" chip?
I'm talking about label not node name. That said I was suggesting
something like:
ds1307: rtc at 68
>
> >
> > > + compatible = "dallas,ds1307";
> > > + reg = <0x68>;
> > > + };
Shawn
^ permalink raw reply
* [PATCH v2 2/2] arm64/mm: add speculative page fault
From: Ganesh Mahendran @ 2018-05-04 6:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1525417069-27401-1-git-send-email-opensource.ganesh@gmail.com>
This patch enables the speculative page fault on the arm64
architecture.
I completed spf porting in 4.9. From the test result,
we can see app launching time improved by about 10% in average.
For the apps which have more than 50 threads, 15% or even more
improvement can be got.
Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
---
v2:
move find_vma() to do_page_fault()
remove IS_ENABLED()
remove fault != VM_FAULT_SIGSEGV check
initilize vma = NULL
---
arch/arm64/mm/fault.c | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 4165485..efd5956 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -320,14 +320,12 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
#define VM_FAULT_BADMAP 0x010000
#define VM_FAULT_BADACCESS 0x020000
-static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
+static int __do_page_fault(struct vm_area_struct *vma, unsigned long addr,
unsigned int mm_flags, unsigned long vm_flags,
struct task_struct *tsk)
{
- struct vm_area_struct *vma;
int fault;
- vma = find_vma(mm, addr);
fault = VM_FAULT_BADMAP;
if (unlikely(!vma))
goto out;
@@ -371,6 +369,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
int fault, major = 0;
unsigned long vm_flags = VM_READ | VM_WRITE;
unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+ struct vm_area_struct *vma = NULL;
if (notify_page_fault(regs, esr))
return 0;
@@ -410,6 +409,16 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
/*
+ * let's try a speculative page fault without grabbing the
+ * mmap_sem.
+ */
+ fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
+ if (fault != VM_FAULT_RETRY) {
+ perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
+ goto done;
+ }
+
+ /*
* As per x86, we may deadlock here. However, since the kernel only
* validly references user space from well defined areas of the code,
* we can bug out early if this is from code which shouldn't.
@@ -431,7 +440,10 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
#endif
}
- fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
+ if (!vma || !can_reuse_spf_vma(vma, addr))
+ vma = find_vma(mm, addr);
+
+ fault = __do_page_fault(vma, addr, mm_flags, vm_flags, tsk);
major |= fault & VM_FAULT_MAJOR;
if (fault & VM_FAULT_RETRY) {
@@ -454,11 +466,20 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
mm_flags |= FAULT_FLAG_TRIED;
+
+ /*
+ * Do not try to reuse this vma and fetch it
+ * again since we will release the mmap_sem.
+ */
+ vma = NULL;
+
goto retry;
}
}
up_read(&mm->mmap_sem);
+done:
+
/*
* Handle the "normal" (no error) case first.
*/
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/2] arm64/mm: define ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT
From: Ganesh Mahendran @ 2018-05-04 6:57 UTC (permalink / raw)
To: linux-arm-kernel
Set ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT for arm64. This
enables Speculative Page Fault handler.
Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
---
v2: remove "if SMP"
---
arch/arm64/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index eb2cf49..b3ca29d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -144,6 +144,7 @@ config ARM64
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select THREAD_INFO_IN_TASK
+ select ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT
help
ARM 64-bit (AArch64) Linux support.
--
1.9.1
^ permalink raw reply related
* [PATCH v5 12/14] cpufreq: Add Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-04 6:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <002a01d3e373$6b6ee410$424cac30$@codeaurora.org>
On 04-05-18, 09:44, ilialin at codeaurora.org wrote:
>
>
> > -----Original Message-----
> > From: Viresh Kumar <viresh.kumar@linaro.org>
> > Sent: Friday, May 4, 2018 09:08
> > To: Ilia Lin <ilialin@codeaurora.org>
> > Cc: mturquette at baylibre.com; sboyd at kernel.org; robh at kernel.org;
> > mark.rutland at arm.com; rjw at rjwysocki.net; lgirdwood at gmail.com;
> > broonie at kernel.org; andy.gross at linaro.org; david.brown at linaro.org;
> > catalin.marinas at arm.com; will.deacon at arm.com; linux-clk at vger.kernel.org;
> > devicetree at vger.kernel.org; linux-kernel at vger.kernel.org; linux-
> > pm at vger.kernel.org; linux-arm-msm at vger.kernel.org; linux-
> > soc at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> > rnayak at codeaurora.org; amit.kucheria at linaro.org;
> > nicolas.dechesne at linaro.org; celster at codeaurora.org;
> > tfinkel at codeaurora.org
> > Subject: Re: [PATCH v5 12/14] cpufreq: Add Kryo CPU scaling driver
> >
> > On 03-05-18, 14:52, Ilia Lin wrote:
> > > In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO
> > > processors, the CPU ferequencies subset and voltage value of each OPP
> > > varies based on the silicon variant in use. Qualcomm Process Voltage
> > > Scaling Tables defines the voltage and frequency value based on the
> > > msm-id in SMEM and speedbin blown in the efuse combination.
> > > The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the
> > > SoC to provide the OPP framework with required information.
> > > This is used to determine the voltage and frequency value for each OPP
> > > of
> > > operating-points-v2 table when it is parsed by the OPP framework.
> > >
> > > Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> > > ---
> > > drivers/cpufreq/Kconfig.arm | 11 +++
> > > drivers/cpufreq/Makefile | 1 +
> > > drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
> > > drivers/cpufreq/qcom-cpufreq-kryo.c | 153
> > > +++++++++++++++++++++++++++++++++++
> > > 4 files changed, 168 insertions(+)
> > > create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
> > >
> > > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> > > index de55c7d..f9da18c 100644
> > > --- a/drivers/cpufreq/Kconfig.arm
> > > +++ b/drivers/cpufreq/Kconfig.arm
> > > @@ -124,6 +124,17 @@ config ARM_OMAP2PLUS_CPUFREQ
> > > depends on ARCH_OMAP2PLUS
> > > default ARCH_OMAP2PLUS
> > >
> > > +config ARM_QCOM_CPUFREQ_KRYO
> > > + tristate "Qualcomm Technologies, Inc. Kryo based CPUFreq"
> >
> > I don't see any reply to Sricharan's query on this being tristate.
>
> Why shouldn't we leave possibility to compile the cpufreq-dt built-in, and
> the qcom-cpufreq-kryo module?
I was not saying this is incorrect, all I am saying is that you never
replied to a comment from one of the reviewers.
And I don't see a reason why this should be a tristate really.
cpufreq-dt is already capable of being a module, all your driver does
is that it creates the cpufreq-dt platform device after setting the
OPP hw properties..
Over that, have you ever tried inserting, then removing and inserting
the driver module again? I feel it will fail.
The reason is that you never provided an exit routine which can get
rid of the platform device created in the first place.
> > > + depends on QCOM_QFPROM
> > > + depends on QCOM_SMEM
> > > + select PM_OPP
> > > + help
> > > + This adds the CPUFreq driver for
> > > + Qualcomm Technologies, Inc. Kryo SoC based boards.
> > > +
> > > + If in doubt, say N.
> > > +
> > > config ARM_S3C_CPUFREQ
> > > bool
> > > help
> > > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index
> > > 8d24ade..fb4a2ec 100644
> > > --- a/drivers/cpufreq/Makefile
> > > +++ b/drivers/cpufreq/Makefile
> > > @@ -65,6 +65,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) +=
> > mvebu-cpufreq.o
> > > obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
> > > obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
> > > obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
> > > +obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-
> > kryo.o
> > > obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
> > > obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
> > > obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
> > > diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c
> > > b/drivers/cpufreq/cpufreq-dt-platdev.c
> > > index 3b585e4..77d6ab8 100644
> > > --- a/drivers/cpufreq/cpufreq-dt-platdev.c
> > > +++ b/drivers/cpufreq/cpufreq-dt-platdev.c
> > > @@ -118,6 +118,9 @@
> > >
> > > { .compatible = "nvidia,tegra124", },
> > >
> > > + { .compatible = "qcom,apq8096", },
> > > + { .compatible = "qcom,msm8996", },
> > > +
> > > { .compatible = "st,stih407", },
> > > { .compatible = "st,stih410", },
> > >
> > > diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c
> > > b/drivers/cpufreq/qcom-cpufreq-kryo.c
> > > new file mode 100644
> > > index 0000000..32371cc
> > > --- /dev/null
> > > +++ b/drivers/cpufreq/qcom-cpufreq-kryo.c
> > > @@ -0,0 +1,153 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
> >
> > Incorrect multi line comment.
>
> This was done as per Bjorn's instruction.
You haven't followed him correctly.
What he asked for is:
// SPDX...
/*
* XXXX
*/
What you have done is:
// SPDX...
/* XXXX
*/
--
viresh
^ permalink raw reply
* [RFC PATCH] ARM: dts: imx: replace underscore with hyphen in aliases name
From: Lothar Waßmann @ 2018-05-04 6:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1525399128-11587-1-git-send-email-shawnguo@kernel.org>
Hi,
On Fri, 4 May 2018 09:58:48 +0800 Shawn Guo wrote:
> It replaces underscore with hyphen in aliases name to fix DTC
> alias_paths warning below, which is seen with various i.MX board dts
> files when W=1 switch is on.
>
> Warning (alias_paths): /aliases: aliases property name must include only lowercase and '-'
>
> Cc: Lothar Wa?mann <LW@KARO-electronics.de>
> Cc: Gary Bisson <gary.bisson@boundarydevices.com>
> Signed-off-by: Shawn Guo <shawnguo@kernel.org>
> ---
> Hi Lothar, Gary,
>
> Per my search on kernel code, this change will not break anything on
> kernel support. But I'm not really sure about the case of bootloaders
> running on the boards. That's why I'm sending it as RFC.
>
> We really want i.MX to be the first-class citizen in the community.
> After a few cycles efforts on cleaning up DTC warnings, this alias_paths
> warning is the last one we see with i.MX dts files. We really want to
> get rid of it and make i.MX dts be DTC warning free.
>
> So please comment whether we can merge this patch.
>
This breaks our U-Boot which searches for the alias names with '_'.
But I think it's not worth keeping the old names alive, since the
problem can easily be fixed by adding the old alias name to the FDT
on affected boards from within U-Boot, if necessary.
Alternatively we could add a:
reg_can_xcvr = "reg-can-xcvr";
[...]
so that the respective aliases can be resolved using both names for a
while.
So:
Acked-By: Lothar Wa?mann <LW@KARO-electronics.de>
> Shawn
>
> arch/arm/boot/dts/imx53-tx53.dtsi | 2 +-
> arch/arm/boot/dts/imx6qdl-tx6.dtsi | 6 +++---
> arch/arm/boot/dts/imx6sx-nitrogen6sx.dts | 4 ++--
> arch/arm/boot/dts/imx6ul-tx6ul-mainboard.dts | 2 +-
> arch/arm/boot/dts/imx6ul-tx6ul.dtsi | 6 +++---
> arch/arm/boot/dts/imx7d-nitrogen7.dts | 4 ++--
> 6 files changed, 12 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/boot/dts/imx53-tx53.dtsi b/arch/arm/boot/dts/imx53-tx53.dtsi
> index 5dd3dd3a2fd0..54cf3e67069a 100644
> --- a/arch/arm/boot/dts/imx53-tx53.dtsi
> +++ b/arch/arm/boot/dts/imx53-tx53.dtsi
> @@ -58,7 +58,7 @@
> can0 = &can2; /* Make the can interface indices consistent with TX28/TX48 modules */
> can1 = &can1;
> ipu = &ipu;
> - reg_can_xcvr = ®_can_xcvr;
> + reg-can-xcvr = ®_can_xcvr;
> usbh1 = &usbh1;
> usbotg = &usbotg;
> };
> diff --git a/arch/arm/boot/dts/imx6qdl-tx6.dtsi b/arch/arm/boot/dts/imx6qdl-tx6.dtsi
> index f015e2d1cf35..a98fb2564c63 100644
> --- a/arch/arm/boot/dts/imx6qdl-tx6.dtsi
> +++ b/arch/arm/boot/dts/imx6qdl-tx6.dtsi
> @@ -50,11 +50,11 @@
> can0 = &can2;
> can1 = &can1;
> ethernet0 = &fec;
> - lcdif_23bit_pins_a = &pinctrl_disp0_1;
> - lcdif_24bit_pins_a = &pinctrl_disp0_2;
> + lcdif-23bit-pins-a = &pinctrl_disp0_1;
> + lcdif-24bit-pins-a = &pinctrl_disp0_2;
> pwm0 = &pwm1;
> pwm1 = &pwm2;
> - reg_can_xcvr = ®_can_xcvr;
> + reg-can-xcvr = ®_can_xcvr;
> stk5led = &user_led;
> usbotg = &usbotg;
> sdhc0 = &usdhc1;
> diff --git a/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts b/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts
> index b58f770c40d9..59e52f504922 100644
> --- a/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts
> +++ b/arch/arm/boot/dts/imx6sx-nitrogen6sx.dts
> @@ -48,8 +48,8 @@
> compatible = "boundary,imx6sx-nitrogen6sx", "fsl,imx6sx";
>
> aliases {
> - fb_lcd = &lcdif1;
> - t_lcd = &t_lcd;
> + fb-lcd = &lcdif1;
> + t-lcd = &t_lcd;
> };
>
> memory at 80000000 {
> diff --git a/arch/arm/boot/dts/imx6ul-tx6ul-mainboard.dts b/arch/arm/boot/dts/imx6ul-tx6ul-mainboard.dts
> index 2d80f7b50bc0..97686097a86e 100644
> --- a/arch/arm/boot/dts/imx6ul-tx6ul-mainboard.dts
> +++ b/arch/arm/boot/dts/imx6ul-tx6ul-mainboard.dts
> @@ -48,7 +48,7 @@
> compatible = "karo,imx6ul-tx6ul", "fsl,imx6ul";
>
> aliases {
> - lcdif_24bit_pins_a = &pinctrl_disp0_3;
> + lcdif-24bit-pins-a = &pinctrl_disp0_3;
> mmc0 = &usdhc1;
> /delete-property/ mmc1;
> serial2 = &uart3;
> diff --git a/arch/arm/boot/dts/imx6ul-tx6ul.dtsi b/arch/arm/boot/dts/imx6ul-tx6ul.dtsi
> index f678d18ad44a..02b5ba42cd59 100644
> --- a/arch/arm/boot/dts/imx6ul-tx6ul.dtsi
> +++ b/arch/arm/boot/dts/imx6ul-tx6ul.dtsi
> @@ -53,10 +53,10 @@
> i2c2 = &i2c1;
> i2c3 = &i2c3;
> i2c4 = &i2c4;
> - lcdif_23bit_pins_a = &pinctrl_disp0_1;
> - lcdif_24bit_pins_a = &pinctrl_disp0_2;
> + lcdif-23bit-pins-a = &pinctrl_disp0_1;
> + lcdif-24bit-pins-a = &pinctrl_disp0_2;
> pwm0 = &pwm5;
> - reg_can_xcvr = ®_can_xcvr;
> + reg-can-xcvr = ®_can_xcvr;
> serial2 = &uart5;
> serial4 = &uart3;
> spi0 = &ecspi2;
> diff --git a/arch/arm/boot/dts/imx7d-nitrogen7.dts b/arch/arm/boot/dts/imx7d-nitrogen7.dts
> index 52167298984d..b8e73b46cddb 100644
> --- a/arch/arm/boot/dts/imx7d-nitrogen7.dts
> +++ b/arch/arm/boot/dts/imx7d-nitrogen7.dts
> @@ -49,8 +49,8 @@
> compatible = "boundary,imx7d-nitrogen7", "fsl,imx7d";
>
> aliases {
> - fb_lcd = &lcdif;
> - t_lcd = &t_lcd;
> + fb-lcd = &lcdif;
> + t-lcd = &t_lcd;
> };
>
> memory at 80000000 {
Lothar Wa?mann
^ permalink raw reply
* [PATCH v4 08/14] dt: qcom: Add opp and thermal to the msm8996
From: ilialin at codeaurora.org @ 2018-05-04 6:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKohpo=cw+2Leuwa6XjEYh4kVYK093A5eckUmPBDutgqeqpvog@mail.gmail.com>
Kryo has single clock per cluster. I define here a shared OPP table per cluster.
> -----Original Message-----
> From: Viresh Kumar <viresh.kumar@linaro.org>
> Sent: Friday, May 4, 2018 09:04
> To: Ilia Lin <ilialin@codeaurora.org>
> Cc: Michael Turquette <mturquette@baylibre.com>; Stephen Boyd
> <sboyd@kernel.org>; Rob Herring <robh@kernel.org>; Mark Rutland
> <mark.rutland@arm.com>; Rafael J. Wysocki <rjw@rjwysocki.net>; Liam
> Girdwood <lgirdwood@gmail.com>; Mark Brown <broonie@kernel.org>;
> Andy Gross <andy.gross@linaro.org>; David Brown
> <david.brown@linaro.org>; Catalin Marinas <catalin.marinas@arm.com>; Will
> Deacon <will.deacon@arm.com>; Linux Clock List <linux-
> clk at vger.kernel.org>; devicetree at vger.kernel.org; Linux Kernel Mailing List
> <linux-kernel@vger.kernel.org>; linux-pm at vger.kernel.org; linux-arm-
> msm at vger.kernel.org; open list:ARM/QUALCOMM SUPPORT <linux-
> soc at vger.kernel.org>; linux-arm-kernel at lists.infradead.org; Nayak,
> Rajendra <rnayak@codeaurora.org>; Amit Kucheria
> <amit.kucheria@linaro.org>; Nicolas Dechesne
> <nicolas.dechesne@linaro.org>; celster at codeaurora.org;
> tfinkel at codeaurora.org
> Subject: Re: [PATCH v4 08/14] dt: qcom: Add opp and thermal to the
> msm8996
>
> On 2 April 2018 at 14:46, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
> >> + cluster0_opp: opp_table0 {
> >> + compatible = "operating-points-v2";
> >> + opp-shared;
> >
> > Is Kryo like krait where CPUs do DVFS independently ? If yes, then
> > opp-shared thing should be dropped.
>
> Have you ever replied to this question ? Sorry, but I am not able to find it in
> my inbox :(
^ permalink raw reply
* [PATCH v5 13/14] dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
From: ilialin at codeaurora.org @ 2018-05-04 6:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180504061123.lf2ffpami23d3q72@vireshk-i7>
bbbb
> -----Original Message-----
> From: Viresh Kumar <viresh.kumar@linaro.org>
> Sent: Friday, May 4, 2018 09:11
> To: Ilia Lin <ilialin@codeaurora.org>
> Cc: mturquette at baylibre.com; sboyd at kernel.org; robh at kernel.org;
> mark.rutland at arm.com; rjw at rjwysocki.net; lgirdwood at gmail.com;
> broonie at kernel.org; andy.gross at linaro.org; david.brown at linaro.org;
> catalin.marinas at arm.com; will.deacon at arm.com; linux-clk at vger.kernel.org;
> devicetree at vger.kernel.org; linux-kernel at vger.kernel.org; linux-
> pm at vger.kernel.org; linux-arm-msm at vger.kernel.org; linux-
> soc at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> rnayak at codeaurora.org; amit.kucheria at linaro.org;
> nicolas.dechesne at linaro.org; celster at codeaurora.org;
> tfinkel at codeaurora.org
> Subject: Re: [PATCH v5 13/14] dt-bindings: cpufreq: Document operating-
> points-v2-kryo-cpu
>
> On 03-05-18, 14:52, Ilia Lin wrote:
> > In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
> > that have KRYO processors, the CPU ferequencies subset and voltage
> > value of each OPP varies based on the silicon variant in use.
> > Qualcomm Technologies, Inc. Process Voltage Scaling Tables defines the
> > voltage and frequency value based on the msm-id in SMEM and speedbin
> > blown in the efuse combination.
> > The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the
> > SoC to provide the OPP framework with required information.
> > This is used to determine the voltage and frequency value for each OPP
> > of
> > operating-points-v2 table when it is parsed by the OPP framework.
> >
> > This change adds documentation.
> >
> > Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> > ---
> > .../devicetree/bindings/opp/kryo-cpufreq.txt | 693
> +++++++++++++++++++++
> > 1 file changed, 693 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
> >
> > diff --git a/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
> > b/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
> > new file mode 100644
> > index 0000000..20cef9d
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
> > @@ -0,0 +1,693 @@
> > +Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings
> > +===================================
> > +
> > +In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
> > +that have KRYO processors, the CPU ferequencies subset and voltage
> > +value of each OPP varies based on the silicon variant in use.
> > +Qualcomm Technologies, Inc. Process Voltage Scaling Tables defines
> > +the voltage and frequency value based on the msm-id in SMEM and
> > +speedbin blown in the efuse combination.
> > +The qcom-cpufreq-kryo driver reads the msm-id and efuse value from
> > +the SoC to provide the OPP framework with required information
> (existing HW bitmap).
> > +This is used to determine the voltage and frequency value for each
> > +OPP of
> > +operating-points-v2 table when it is parsed by the OPP framework.
> > +
> > +Required properties:
> > +--------------------
> > +In 'cpus' nodes:
> > +- operating-points-v2: Phandle to the operating-points-v2 table to use.
> > +
> > +In 'operating-points-v2' table:
> > +- compatible: Should be
> > + - 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.
> > +- nvmem-cells: A phandle pointing to a nvmem-cells node representing
> the
> > + efuse registers that has information about the
> > + speedbin that is used to select the right frequency/voltage
> > + value pair.
> > + Please refer the for nvmem-cells
> > + bindings
> Documentation/devicetree/bindings/nvmem/nvmem.txt
> > + and also examples below.
> > +
> > +In every OPP node:
> > +- opp-supported-hw: A single 32 bit bitmap value, representing
> compatible HW.
> > + Bitmap:
> > + 0: MSM8996 V3, speedbin 0
> > + 1: MSM8996 V3, speedbin 1
> > + 2: MSM8996 V3, speedbin 2
> > + 3: unused
> > + 4: MSM8996 SG, speedbin 0
> > + 5: MSM8996 SG, speedbin 1
> > + 6: MSM8996 SG, speedbin 2
> > + 7-31: unused
> > +
> > +Example 1:
> > +---------
> > +
> > + cpus {
> > + #address-cells = <2>;
> > + #size-cells = <0>;
> > +
> > + CPU0: cpu at 0 {
> > + device_type = "cpu";
> > + compatible = "qcom,kryo";
> > + reg = <0x0 0x0>;
> > + enable-method = "psci";
> > + clocks = <&kryocc 0>;
> > + cpu-supply = <&pm8994_s11_saw>;
> > + operating-points-v2 = <&cluster0_opp>;
> > + /* cooling options */
> > + cooling-min-level = <0>;
> > + cooling-max-level = <15>;
>
> cooling min/max aren't required anymore, as I told you in the previous
> version :)
Sure, I removed them in the DT, but forgot in the documentation. Will fix.
>
> > + cluster0_opp: opp_table0 {
> > + compatible = "operating-points-v2-kryo-cpu";
> > + nvmem-cells = <&speedbin_efuse>;
> > + opp-shared;
> > +
> > + opp-307200000 {
> > + opp-hz = /bits/ 64 < 307200000 >;
>
> You fixed spacing around frequency values in the dts but not here.
Same as above.
>
> > + opp-microvolt = <905000 905000 1140000>;
> > + opp-supported-hw = <0x77>;
> > + clock-latency-ns = <200000>;
> > + };
>
> --
> viresh
^ permalink raw reply
* [PATCH v10 1/2] dt-bindings: media: Add Allwinner V3s Camera Sensor Interface (CSI)
From: Yong Deng @ 2018-05-04 6:46 UTC (permalink / raw)
To: linux-arm-kernel
Add binding documentation for Allwinner V3s CSI.
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Yong Deng <yong.deng@magewell.com>
---
.../devicetree/bindings/media/sun6i-csi.txt | 59 ++++++++++++++++++++++
1 file changed, 59 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 000000000000..2ff47a9507a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt
@@ -0,0 +1,59 @@
+Allwinner V3s Camera Sensor Interface
+-------------------------------------
+
+Allwinner V3s SoC features two CSI module. CSI0 is used for MIPI CSI-2
+interface and CSI1 is used for parallel 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
+
+Each CSI node should contain one 'port' child node with one child 'endpoint'
+node, according to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt. As mentioned
+above, the endpoint's bus type should be MIPI CSI-2 for CSI0 and parallel or
+Bt656 for CSI1.
+
+Endpoint node properties for CSI1
+---------------------------------
+
+- remote-endpoint : (required) a phandle to the bus receiver's endpoint
+ node
+- bus-width: : (required) must be 8, 10, 12 or 16
+- pclk-sample : (optional) (default: sample on falling edge)
+- hsync-active : (only required for parallel)
+- vsync-active : (only required for parallel)
+
+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 {
+ /* Parallel bus endpoint */
+ csi1_ep: endpoint {
+ remote-endpoint = <&adv7611_ep>;
+ bus-width = <16>;
+
+ /* If hsync-active/vsync-active are missing,
+ embedded BT.656 sync is used */
+ hsync-active = <0>; /* Active low */
+ vsync-active = <0>; /* Active low */
+ pclk-sample = <1>; /* Rising */
+ };
+ };
+};
--
1.8.3.1
^ permalink raw reply related
* [PATCH v5 12/14] cpufreq: Add Kryo CPU scaling driver
From: ilialin at codeaurora.org @ 2018-05-04 6:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180504060827.7bddb53ln2ogwcvv@vireshk-i7>
> -----Original Message-----
> From: Viresh Kumar <viresh.kumar@linaro.org>
> Sent: Friday, May 4, 2018 09:08
> To: Ilia Lin <ilialin@codeaurora.org>
> Cc: mturquette at baylibre.com; sboyd at kernel.org; robh at kernel.org;
> mark.rutland at arm.com; rjw at rjwysocki.net; lgirdwood at gmail.com;
> broonie at kernel.org; andy.gross at linaro.org; david.brown at linaro.org;
> catalin.marinas at arm.com; will.deacon at arm.com; linux-clk at vger.kernel.org;
> devicetree at vger.kernel.org; linux-kernel at vger.kernel.org; linux-
> pm at vger.kernel.org; linux-arm-msm at vger.kernel.org; linux-
> soc at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> rnayak at codeaurora.org; amit.kucheria at linaro.org;
> nicolas.dechesne at linaro.org; celster at codeaurora.org;
> tfinkel at codeaurora.org
> Subject: Re: [PATCH v5 12/14] cpufreq: Add Kryo CPU scaling driver
>
> On 03-05-18, 14:52, Ilia Lin wrote:
> > In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO
> > processors, the CPU ferequencies subset and voltage value of each OPP
> > varies based on the silicon variant in use. Qualcomm Process Voltage
> > Scaling Tables defines the voltage and frequency value based on the
> > msm-id in SMEM and speedbin blown in the efuse combination.
> > The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the
> > SoC to provide the OPP framework with required information.
> > This is used to determine the voltage and frequency value for each OPP
> > of
> > operating-points-v2 table when it is parsed by the OPP framework.
> >
> > Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> > ---
> > drivers/cpufreq/Kconfig.arm | 11 +++
> > drivers/cpufreq/Makefile | 1 +
> > drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
> > drivers/cpufreq/qcom-cpufreq-kryo.c | 153
> > +++++++++++++++++++++++++++++++++++
> > 4 files changed, 168 insertions(+)
> > create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
> >
> > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> > index de55c7d..f9da18c 100644
> > --- a/drivers/cpufreq/Kconfig.arm
> > +++ b/drivers/cpufreq/Kconfig.arm
> > @@ -124,6 +124,17 @@ config ARM_OMAP2PLUS_CPUFREQ
> > depends on ARCH_OMAP2PLUS
> > default ARCH_OMAP2PLUS
> >
> > +config ARM_QCOM_CPUFREQ_KRYO
> > + tristate "Qualcomm Technologies, Inc. Kryo based CPUFreq"
>
> I don't see any reply to Sricharan's query on this being tristate.
Why shouldn't we leave possibility to compile the cpufreq-dt built-in, and
the qcom-cpufreq-kryo module?
>
> > + depends on QCOM_QFPROM
> > + depends on QCOM_SMEM
> > + select PM_OPP
> > + help
> > + This adds the CPUFreq driver for
> > + Qualcomm Technologies, Inc. Kryo SoC based boards.
> > +
> > + If in doubt, say N.
> > +
> > config ARM_S3C_CPUFREQ
> > bool
> > help
> > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index
> > 8d24ade..fb4a2ec 100644
> > --- a/drivers/cpufreq/Makefile
> > +++ b/drivers/cpufreq/Makefile
> > @@ -65,6 +65,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) +=
> mvebu-cpufreq.o
> > obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
> > obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
> > obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
> > +obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-
> kryo.o
> > obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
> > obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
> > obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
> > diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c
> > b/drivers/cpufreq/cpufreq-dt-platdev.c
> > index 3b585e4..77d6ab8 100644
> > --- a/drivers/cpufreq/cpufreq-dt-platdev.c
> > +++ b/drivers/cpufreq/cpufreq-dt-platdev.c
> > @@ -118,6 +118,9 @@
> >
> > { .compatible = "nvidia,tegra124", },
> >
> > + { .compatible = "qcom,apq8096", },
> > + { .compatible = "qcom,msm8996", },
> > +
> > { .compatible = "st,stih407", },
> > { .compatible = "st,stih410", },
> >
> > diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c
> > b/drivers/cpufreq/qcom-cpufreq-kryo.c
> > new file mode 100644
> > index 0000000..32371cc
> > --- /dev/null
> > +++ b/drivers/cpufreq/qcom-cpufreq-kryo.c
> > @@ -0,0 +1,153 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
>
> Incorrect multi line comment.
This was done as per Bjorn's instruction.
>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + modify
> > + * it under the terms of the GNU General Public License version 2 and
> > + * only version 2 as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/cpu.h>
> > +#include <linux/err.h>
> > +#include <linux/init.h>
> > +//#include <linux/io.h>
>
> ??
Not good. Will fix this.bbbbbbbb
>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_opp.h>
> > +#include <linux/slab.h>
> > +#include <linux/soc/qcom/smem.h>
> > +
> > +#define MSM_ID_SMEM 137
> > +#define SILVER_LEAD 0
> > +#define GOLD_LEAD 2
> > +
> > +enum _msm_id {
> > + MSM8996V3 = 0xF6ul,
> > + APQ8096V3 = 0x123ul,
> > + MSM8996SG = 0x131ul,
> > + APQ8096SG = 0x138ul,
> > +};
> > +
> > +enum _msm8996_version {
> > + MSM8996_V3,
> > + MSM8996_SG,
> > + NUM_OF_MSM8996_VERSIONS,
> > +};
> > +
> > +static enum _msm8996_version __init
> > +qcom_cpufreq_kryo_get_msm_id(void)
> > +{
> > + size_t len;
> > + u32 *msm_id;
> > + enum _msm8996_version version;
> > +
> > + msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY,
> MSM_ID_SMEM, &len);
> > + /* The first 4 bytes are format, next to them is the actual msm-id
*/
> > + msm_id++;
> > +
> > + switch ((enum _msm_id)*msm_id) {
> > + case MSM8996V3:
> > + case APQ8096V3:
> > + version = MSM8996_V3;
> > + break;
> > + case MSM8996SG:
> > + case APQ8096SG:
> > + version = MSM8996_SG;
> > + break;
> > + default:
> > + version = NUM_OF_MSM8996_VERSIONS;
> > + }
> > +
> > + return version;
> > +}
> > +
> > +static int __init qcom_cpufreq_kryo_driver_init(void)
> > +{
> > + size_t len;
> > + int ret;
> > + u32 versions;
> > + enum _msm8996_version msm8996_version;
> > + u8 *speedbin;
> > + struct platform_device *pdev;
> > + struct device *cpu_dev;
> > + struct device_node *np;
> > + struct nvmem_cell *speedbin_nvmem;
> > + struct opp_table *opp_temp = NULL;
> > +
> > + cpu_dev = get_cpu_device(SILVER_LEAD);
> > + if (IS_ERR_OR_NULL(cpu_dev))
> > + return PTR_ERR(cpu_dev);
> > +
> > + msm8996_version = qcom_cpufreq_kryo_get_msm_id();
> > + if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
> > + dev_err(cpu_dev, "Not Snapdragon 820/821!");
> > + return -ENODEV;
> > + }
> > +
> > + np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
> > + if (IS_ERR_OR_NULL(np))
> > + return PTR_ERR(np);
> > +
> > + if (!of_device_is_compatible(np, "operating-points-v2-kryo-cpu")) {
> > + ret = -ENOENT;
> > + goto free_np;
> > + }
> > +
> > + speedbin_nvmem = of_nvmem_cell_get(np, NULL);
> > + if (IS_ERR(speedbin_nvmem)) {
> > + ret = PTR_ERR(speedbin_nvmem);
> > + dev_err(cpu_dev, "Could not get nvmem cell: %d\n", ret);
> > + goto free_np;
> > + }
> > +
> > + speedbin = nvmem_cell_read(speedbin_nvmem, &len);
> > +
> > + switch (msm8996_version) {
> > + case MSM8996_V3:
> > + versions = 1 << (unsigned int)(*speedbin);
> > + break;
> > + case MSM8996_SG:
> > + versions = 1 << ((unsigned int)(*speedbin) + 4);
> > + break;
> > + default:
> > + BUG();
> > + break;
> > + }
> > +
> > + ret = PTR_ERR_OR_ZERO(opp_temp = \
> > +
> dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> > + if (0 > ret)
> > + goto free_np;
> > +
> > + cpu_dev = get_cpu_device(GOLD_LEAD);
> > + ret =
> PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(cpu_dev,&versions,1
> ));
> > + if (0 > ret)
> > + goto put_supported_hw_silver;
> > +
> > + of_node_put(np);
> > +
> > + pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
> > + if (IS_ERR(pdev)) {
> > + return PTR_ERR(pdev);
>
> Don't need to free resources on error here ?
You are right. Will fix.
>
> > + }
> > +
> > + return 0;
> > +
> > +put_supported_hw_silver:
> > + dev_pm_opp_put_supported_hw(opp_temp);
> > +
> > +free_np:
> > + of_node_put(np);
> > +
> > + return ret;
> > +}
> > +late_initcall(qcom_cpufreq_kryo_driver_init);
> > +
> > +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq
> > +driver"); MODULE_LICENSE("GPL v2");
> > --
> > 1.9.1
>
> --
> viresh
^ permalink raw reply
* [PATCH v10 0/2] Initial Allwinner V3s CSI Support
From: Yong Deng @ 2018-05-04 6:44 UTC (permalink / raw)
To: linux-arm-kernel
This patchset add initial support for Allwinner V3s CSI.
Allwinner V3s SoC features two CSI module. CSI0 is used for MIPI CSI-2
interface and CSI1 is used for parallel interface. This is not
documented in datasheet but by test and guess.
This patchset implement a v4l2 framework driver and add a binding
documentation for it.
Currently, the driver only support the parallel interface. And has been
tested with a BT1120 signal which generating from FPGA. The following
fetures are not support with this patchset:
- ISP
- MIPI-CSI2
- Master clock for camera sensor
- Power regulator for the front end IC
Changes in v10:
* change sun6i_csi_is_format_support to sun6i_csi_is_format_supported
* using media_entity_get_fwnode_pad to get source pad
* switch media_entity_call to media_entity_setup_link
* remove V4L2_INPUT_TYPE_CAMERA
Changes in v9:
* Merge the patchs from Maxime:
a. Fill dma_pfn_offset to accomodate for the RAM offset
b. Reduce the error level
c. Pass the sun6i_csi_dev pointer to our helpers
d. Don't emit a warning when the configured format isn't found
e. Support the YUYV format properly
f. Invert the polaritie of all signals
g. Expose controls on the v4l2_device
Changes in v8:
* Revert to v6 and add 'hack' for PHYS_OFFSET.
Changes in v7:
* Add 'depends on ARM' in Kconfig to avoid built with non-ARM arch.
Changes in v6:
* Add Rob Herring's review tag.
* Fix a NULL pointer dereference by picking Maxime Ripard's patch.
* Add Maxime Ripard's test tag.
Changes in v5:
* Using the new SPDX tags.
* Fix MODULE_LICENSE.
* Add many default cases and warning messages.
* Detail the parallel bus properties
* Fix some spelling and syntax mistakes.
Changes in v4:
* Deal with the CSI 'INNER QUEUE'.
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.
* Fix: using a wrong mbus_code when getting the supported formats
* Change all fourcc to pixformat
* Change some function names
Changes in v3:
* Get rid of struct sun6i_csi_ops
* Move sun6i-csi to new directory drivers/media/platform/sunxi
* Merge sun6i_csi.c and sun6i_csi_v3s.c into sun6i_csi.c
* Use generic fwnode endpoints parser
* Only support a single subdev to make things simple
* Many complaintion fix
Changes in v2:
* Change sunxi-csi to sun6i-csi
* Rebase to media_tree master branch
Following is the 'v4l2-compliance -s -f' output, I have test this
with both interlaced and progressive signal:
# ./v4l2-compliance -s -f
v4l2-compliance SHA : 6049ea8bd64f9d78ef87ef0c2b3dc9b5de1ca4a1
Driver Info:
Driver name : sun6i-video
Card type : sun6i-csi
Bus info : platform:csi
Driver version: 4.15.0
Capabilities : 0x84200001
Video Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
Compliance test for device /dev/video0 (not using libv4l2):
Required ioctls:
test VIDIOC_QUERYCAP: OK
Allow for multiple opens:
test second video open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Test input 0:
Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
Test input 0:
Streaming ioctls:
test read/write: OK (Not Supported)
test MMAP: OK
test USERPTR: OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device
Stream using all formats:
test MMAP for Format HM12, Frame Size 1280x720:
Stride 1920, Field None: OK
test MMAP for Format NV12, Frame Size 1280x720:
Stride 1920, Field None: OK
test MMAP for Format NV21, Frame Size 1280x720:
Stride 1920, Field None: OK
test MMAP for Format YU12, Frame Size 1280x720:
Stride 1920, Field None: OK
test MMAP for Format YV12, Frame Size 1280x720:
Stride 1920, Field None: OK
test MMAP for Format NV16, Frame Size 1280x720:
Stride 2560, Field None: OK
test MMAP for Format NV61, Frame Size 1280x720:
Stride 2560, Field None: OK
test MMAP for Format 422P, Frame Size 1280x720:
Stride 2560, Field None: OK
Total: 54, Succeeded: 54, Failed: 0, Warnings: 0
Yong Deng (2):
dt-bindings: media: Add Allwinner V3s Camera Sensor Interface (CSI)
media: V3s: Add support for Allwinner CSI.
.../devicetree/bindings/media/sun6i-csi.txt | 59 ++
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 | 931 +++++++++++++++++++++
drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 145 ++++
.../media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 196 +++++
.../media/platform/sunxi/sun6i-csi/sun6i_video.c | 767 +++++++++++++++++
.../media/platform/sunxi/sun6i-csi/sun6i_video.h | 53 ++
11 files changed, 2174 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/sun6i-csi.txt
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
--
1.8.3.1
^ permalink raw reply
* [PATCH] tty/serial: atmel: use port->name as name in request_irq()
From: Richard Genoud @ 2018-05-04 6:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <921729c6-7c3f-11cd-8634-044a5c52f810@sorico.fr>
On 03/05/2018 15:34, Richard Genoud wrote:
> On 03/05/2018 14:44, Sebastian Andrzej Siewior wrote:
>> On 2018-05-03 14:36:03 [+0200], Richard Genoud wrote:
>>> Could resend your patch with Fixes: in the commit message ?
>>
>> will do shortly.
Actually, this fix can only be applied on stable kernels 4.14+, because
uart_port->name was introduced in 4.12 by commit f7048b15900f ("tty:
serial_core: Add name field to uart_port struct")
>>
>>> BTW, I didn't manage to reproduce the behavior you describe, could you
>>> give me your .config and describe a little more how you manage to
>>> trigger this bug ?
>> .config sent offlist.
>> Did you not reproduce this even with the second/debug patch I've sent?
> Nope. (I tried on a sam9g35 with buildroot/busybox)
>
> I have a sama5d3 with a debian as a home server, I'll give it a try.
>
>>
>>> (do you use the console on serial debug ? which board ? )
>> up to date debian sid booting with systemd. After boot completed I do
>> "cat /proc/interrupts" and check for the "gone" string.
>> This is
>> [ 0.000000] OF: fdt:Machine model: SAMA5D3 Xplained
>> [ 0.000000] Kernel command line: earlyprintk=ttyS0,115200 console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait
>> [ 0.520000] AT91: Detected SoC family: sama5d3
>> [ 0.520000] AT91: Detected SoC: sama5d36, revision 2
>>
>> at91-sama5d3_xplained.dtb
>>
>> Is this enough?
>
> Great,
>
> Thanks !
>
^ permalink raw reply
* [PATCH 2/2] arm64/mm: add speculative page fault
From: Ganesh Mahendran @ 2018-05-04 6:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <871seunmj9.fsf@e105922-lin.cambridge.arm.com>
2018-05-02 22:46 GMT+08:00 Punit Agrawal <punit.agrawal@arm.com>:
> Hi Ganesh,
>
> I was looking at evaluating speculative page fault handling on arm64 and
> noticed your patch.
>
> Some comments below -
Thanks for your review.
>
> Ganesh Mahendran <opensource.ganesh@gmail.com> writes:
>
>> This patch enables the speculative page fault on the arm64
>> architecture.
>>
>> I completed spf porting in 4.9. From the test result,
>> we can see app launching time improved by about 10% in average.
>> For the apps which have more than 50 threads, 15% or even more
>> improvement can be got.
>>
>> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
>> ---
>> This patch is on top of Laurent's v10 spf
>> ---
>> arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
>> 1 file changed, 35 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
>> index 4165485..e7992a3 100644
>> --- a/arch/arm64/mm/fault.c
>> +++ b/arch/arm64/mm/fault.c
>> @@ -322,11 +322,13 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
>>
>> static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
>> unsigned int mm_flags, unsigned long vm_flags,
>> - struct task_struct *tsk)
>> + struct task_struct *tsk, struct vm_area_struct *vma)
>> {
>> - struct vm_area_struct *vma;
>> int fault;
>>
>> + if (!vma || !can_reuse_spf_vma(vma, addr))
>> + vma = find_vma(mm, addr);
>> +
>
> It would be better to move this hunk to do_page_fault().
>
> It'll help localise the fact that handle_speculative_fault() is a
> stateful call which needs a corresponding can_reuse_spf_vma() to
> properly update the vma reference counting.
Yes, your suggestion is better.
>
>
>> vma = find_vma(mm, addr);
>
> Remember to drop this call in the next version. As it stands the call
> the find_vma() needlessly gets duplicated.
Will fix
>
>> fault = VM_FAULT_BADMAP;
>> if (unlikely(!vma))
>> @@ -371,6 +373,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>> int fault, major = 0;
>> unsigned long vm_flags = VM_READ | VM_WRITE;
>> unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
>> + struct vm_area_struct *vma;
>>
>> if (notify_page_fault(regs, esr))
>> return 0;
>> @@ -409,6 +412,25 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>
>> perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
>>
>> + if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {
>
> You don't need the IS_ENABLED() check. The alternate implementation of
> handle_speculative_fault() when CONFIG_SPECULATIVE_PAGE_FAULT is not
> enabled takes care of this.
Will fix
>
>> + fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
>> + /*
>> + * Page fault is done if VM_FAULT_RETRY is not returned.
>> + * But if the memory protection keys are active, we don't know
>> + * if the fault is due to key mistmatch or due to a
>> + * classic protection check.
>> + * To differentiate that, we will need the VMA we no
>> + * more have, so let's retry with the mmap_sem held.
>> + */
>
> As there is no support for memory protection keys on arm64 most of this
> comment can be dropped.
will fix
>
>> + if (fault != VM_FAULT_RETRY &&
>> + fault != VM_FAULT_SIGSEGV) {
>
> Not sure if you need the VM_FAULT_SIGSEGV here.
>
>> + perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
>> + goto done;
>> + }
>> + } else {
>> + vma = NULL;
>> + }
>> +
>
> If vma is initiliased to NULL during declaration, the else part can be
> dropped.
will fix
>
>> /*
>> * As per x86, we may deadlock here. However, since the kernel only
>> * validly references user space from well defined areas of the code,
>> @@ -431,7 +453,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>> #endif
>> }
>>
>> - fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
>> + fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
>> major |= fault & VM_FAULT_MAJOR;
>>
>> if (fault & VM_FAULT_RETRY) {
>> @@ -454,11 +476,21 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>> if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
>> mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
>> mm_flags |= FAULT_FLAG_TRIED;
>> +
>> + /*
>> + * Do not try to reuse this vma and fetch it
>> + * again since we will release the mmap_sem.
>> + */
>> + if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
>> + vma = NULL;
>
> Please drop the IS_ENABLED() check.
will fix
>
> Thanks,
> Punit
>
>> +
>> goto retry;
>> }
>> }
>> up_read(&mm->mmap_sem);
>>
>> +done:
>> +
>> /*
>> * Handle the "normal" (no error) case first.
>> */
^ permalink raw reply
* [PATCH 2/2] arm64/mm: add speculative page fault
From: Ganesh Mahendran @ 2018-05-04 6:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <9e7ab02c-a9af-71ed-afda-108e3b26b2ef@linux.vnet.ibm.com>
2018-05-02 17:07 GMT+08:00 Laurent Dufour <ldufour@linux.vnet.ibm.com>:
> On 02/05/2018 09:54, Ganesh Mahendran wrote:
>> This patch enables the speculative page fault on the arm64
>> architecture.
>>
>> I completed spf porting in 4.9. From the test result,
>> we can see app launching time improved by about 10% in average.
>> For the apps which have more than 50 threads, 15% or even more
>> improvement can be got.
>
> Thanks Ganesh,
>
> That's a great improvement, could you please provide details about the apps and
> the hardware you used ?
We run spf on Qcom SDM845(kernel 4.9). Below is app(popular in China)
list we tested:
------
com.tencent.mobileqq
com.tencent.qqmusic
com.tencent.mtt
com.UCMobile
com.qiyi.video
com.baidu.searchbox
com.baidu.BaiduMap
tv.danmaku.bili
com.sdu.didi.psnger
com.ss.android.ugc.aweme
air.tv.douyu.android
me.ele
com.autonavi.minimap
com.duowan.kiwi
com.v.study
com.qqgame.hlddz
com.ss.android.article.lite
com.jingdong.app.mall
com.tencent.tmgp.pubgmhd
com.kugou.android
com.kuaikan.comic
com.hunantv.imgo.activity
com.mt.mtxx.mtxx
com.sankuai.meituan
com.sankuai.meituan.takeoutnew
com.tencent.karaoke
com.taobao.taobao
com.tencent.qqlive
com.tmall.wireless
com.tencent.tmgp.sgame
com.netease.cloudmusic
com.sina.weibo
com.tencent.mm
com.immomo.momo
com.xiaomi.hm.health
com.youku.phone
com.eg.android.AlipayGphone
com.meituan.qcs.c.android
------
We will do more test of the V10 spf.
>
>>
>> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
>> ---
>> This patch is on top of Laurent's v10 spf
>> ---
>> arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
>> 1 file changed, 35 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
>> index 4165485..e7992a3 100644
>> --- a/arch/arm64/mm/fault.c
>> +++ b/arch/arm64/mm/fault.c
>> @@ -322,11 +322,13 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
>>
>> static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
>> unsigned int mm_flags, unsigned long vm_flags,
>> - struct task_struct *tsk)
>> + struct task_struct *tsk, struct vm_area_struct *vma)
>> {
>> - struct vm_area_struct *vma;
>> int fault;
>>
>> + if (!vma || !can_reuse_spf_vma(vma, addr))
>> + vma = find_vma(mm, addr);
>> +
>> vma = find_vma(mm, addr);
>> fault = VM_FAULT_BADMAP;
>> if (unlikely(!vma))
>> @@ -371,6 +373,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>> int fault, major = 0;
>> unsigned long vm_flags = VM_READ | VM_WRITE;
>> unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
>> + struct vm_area_struct *vma;
>>
>> if (notify_page_fault(regs, esr))
>> return 0;
>> @@ -409,6 +412,25 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>
>> perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
>>
>> + if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {
>
> As suggested by Punit in his v10's review, the test on
> CONFIG_SPECULATIVE_PAGE_FAULT is not needed as handle_speculative_fault() is
> defined to return VM_FAULT_RETRY is the config is not set.
Thanks, will fix.
>
>> + fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
>> + /*
>> + * Page fault is done if VM_FAULT_RETRY is not returned.
>> + * But if the memory protection keys are active, we don't know
>> + * if the fault is due to key mistmatch or due to a
>> + * classic protection check.
>> + * To differentiate that, we will need the VMA we no
>> + * more have, so let's retry with the mmap_sem held.
>> + */
>
> The check of VM_FAULT_SIGSEGV was needed on ppc64 because of the memory
> protection key support, but as far as I know, this is not the case on arm64.
> Isn't it ?
Yes, wil fix.
>
>> + if (fault != VM_FAULT_RETRY &&
>> + fault != VM_FAULT_SIGSEGV) {
>> + perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
>> + goto done;
>> + }
>> + } else {
>> + vma = NULL;
>> + }
>> +
>> /*
>> * As per x86, we may deadlock here. However, since the kernel only
>> * validly references user space from well defined areas of the code,
>> @@ -431,7 +453,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>> #endif
>> }
>>
>> - fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
>> + fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
>> major |= fault & VM_FAULT_MAJOR;
>>
>> if (fault & VM_FAULT_RETRY) {
>> @@ -454,11 +476,21 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>> if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
>> mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
>> mm_flags |= FAULT_FLAG_TRIED;
>> +
>> + /*
>> + * Do not try to reuse this vma and fetch it
>> + * again since we will release the mmap_sem.
>> + */
>> + if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
>> + vma = NULL;
>> +
>> goto retry;
>> }
>> }
>> up_read(&mm->mmap_sem);
>>
>> +done:
>> +
>> /*
>> * Handle the "normal" (no error) case first.
>> */
>>
>
^ permalink raw reply
* [PATCH v5 13/14] dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
From: Viresh Kumar @ 2018-05-04 6:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1525348355-25471-14-git-send-email-ilialin@codeaurora.org>
On 03-05-18, 14:52, Ilia Lin wrote:
> In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
> that have KRYO processors, the CPU ferequencies subset and voltage value
> of each OPP varies based on the silicon variant in use.
> Qualcomm Technologies, Inc. Process Voltage Scaling Tables
> defines the voltage and frequency value based on the msm-id in SMEM
> and speedbin blown in the efuse combination.
> The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> to provide the OPP framework with required information.
> This is used to determine the voltage and frequency value for each OPP of
> operating-points-v2 table when it is parsed by the OPP framework.
>
> This change adds documentation.
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> .../devicetree/bindings/opp/kryo-cpufreq.txt | 693 +++++++++++++++++++++
> 1 file changed, 693 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
>
> diff --git a/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt b/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
> new file mode 100644
> index 0000000..20cef9d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
> @@ -0,0 +1,693 @@
> +Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings
> +===================================
> +
> +In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
> +that have KRYO processors, the CPU ferequencies subset and voltage value
> +of each OPP varies based on the silicon variant in use.
> +Qualcomm Technologies, Inc. Process Voltage Scaling Tables
> +defines the voltage and frequency value based on the msm-id in SMEM
> +and speedbin blown in the efuse combination.
> +The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> +to provide the OPP framework with required information (existing HW bitmap).
> +This is used to determine the voltage and frequency value for each OPP of
> +operating-points-v2 table when it is parsed by the OPP framework.
> +
> +Required properties:
> +--------------------
> +In 'cpus' nodes:
> +- operating-points-v2: Phandle to the operating-points-v2 table to use.
> +
> +In 'operating-points-v2' table:
> +- compatible: Should be
> + - 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.
> +- nvmem-cells: A phandle pointing to a nvmem-cells node representing the
> + efuse registers that has information about the
> + speedbin that is used to select the right frequency/voltage
> + value pair.
> + Please refer the for nvmem-cells
> + bindings Documentation/devicetree/bindings/nvmem/nvmem.txt
> + and also examples below.
> +
> +In every OPP node:
> +- opp-supported-hw: A single 32 bit bitmap value, representing compatible HW.
> + Bitmap:
> + 0: MSM8996 V3, speedbin 0
> + 1: MSM8996 V3, speedbin 1
> + 2: MSM8996 V3, speedbin 2
> + 3: unused
> + 4: MSM8996 SG, speedbin 0
> + 5: MSM8996 SG, speedbin 1
> + 6: MSM8996 SG, speedbin 2
> + 7-31: unused
> +
> +Example 1:
> +---------
> +
> + cpus {
> + #address-cells = <2>;
> + #size-cells = <0>;
> +
> + CPU0: cpu at 0 {
> + device_type = "cpu";
> + compatible = "qcom,kryo";
> + reg = <0x0 0x0>;
> + enable-method = "psci";
> + clocks = <&kryocc 0>;
> + cpu-supply = <&pm8994_s11_saw>;
> + operating-points-v2 = <&cluster0_opp>;
> + /* cooling options */
> + cooling-min-level = <0>;
> + cooling-max-level = <15>;
cooling min/max aren't required anymore, as I told you in the previous
version :)
> + cluster0_opp: opp_table0 {
> + compatible = "operating-points-v2-kryo-cpu";
> + nvmem-cells = <&speedbin_efuse>;
> + opp-shared;
> +
> + opp-307200000 {
> + opp-hz = /bits/ 64 < 307200000 >;
You fixed spacing around frequency values in the dts but not here.
> + opp-microvolt = <905000 905000 1140000>;
> + opp-supported-hw = <0x77>;
> + clock-latency-ns = <200000>;
> + };
--
viresh
^ permalink raw reply
* [PATCH v5 12/14] cpufreq: Add Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-04 6:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1525348355-25471-13-git-send-email-ilialin@codeaurora.org>
On 03-05-18, 14:52, Ilia Lin wrote:
> In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
> the CPU ferequencies subset and voltage value of each OPP varies
> based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
> defines the voltage and frequency value based on the msm-id in SMEM
> and speedbin blown in the efuse combination.
> The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> to provide the OPP framework with required information.
> This is used to determine the voltage and frequency value for each OPP of
> operating-points-v2 table when it is parsed by the OPP framework.
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> drivers/cpufreq/Kconfig.arm | 11 +++
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
> drivers/cpufreq/qcom-cpufreq-kryo.c | 153 +++++++++++++++++++++++++++++++++++
> 4 files changed, 168 insertions(+)
> create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
>
> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> index de55c7d..f9da18c 100644
> --- a/drivers/cpufreq/Kconfig.arm
> +++ b/drivers/cpufreq/Kconfig.arm
> @@ -124,6 +124,17 @@ config ARM_OMAP2PLUS_CPUFREQ
> depends on ARCH_OMAP2PLUS
> default ARCH_OMAP2PLUS
>
> +config ARM_QCOM_CPUFREQ_KRYO
> + tristate "Qualcomm Technologies, Inc. Kryo based CPUFreq"
I don't see any reply to Sricharan's query on this being tristate.
> + depends on QCOM_QFPROM
> + depends on QCOM_SMEM
> + select PM_OPP
> + help
> + This adds the CPUFreq driver for
> + Qualcomm Technologies, Inc. Kryo SoC based boards.
> +
> + If in doubt, say N.
> +
> config ARM_S3C_CPUFREQ
> bool
> help
> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
> index 8d24ade..fb4a2ec 100644
> --- a/drivers/cpufreq/Makefile
> +++ b/drivers/cpufreq/Makefile
> @@ -65,6 +65,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
> obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
> obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
> obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
> +obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o
> obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
> obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
> obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
> diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
> index 3b585e4..77d6ab8 100644
> --- a/drivers/cpufreq/cpufreq-dt-platdev.c
> +++ b/drivers/cpufreq/cpufreq-dt-platdev.c
> @@ -118,6 +118,9 @@
>
> { .compatible = "nvidia,tegra124", },
>
> + { .compatible = "qcom,apq8096", },
> + { .compatible = "qcom,msm8996", },
> +
> { .compatible = "st,stih407", },
> { .compatible = "st,stih410", },
>
> diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-kryo.c
> new file mode 100644
> index 0000000..32371cc
> --- /dev/null
> +++ b/drivers/cpufreq/qcom-cpufreq-kryo.c
> @@ -0,0 +1,153 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
Incorrect multi line comment.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/cpu.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +//#include <linux/io.h>
??
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/slab.h>
> +#include <linux/soc/qcom/smem.h>
> +
> +#define MSM_ID_SMEM 137
> +#define SILVER_LEAD 0
> +#define GOLD_LEAD 2
> +
> +enum _msm_id {
> + MSM8996V3 = 0xF6ul,
> + APQ8096V3 = 0x123ul,
> + MSM8996SG = 0x131ul,
> + APQ8096SG = 0x138ul,
> +};
> +
> +enum _msm8996_version {
> + MSM8996_V3,
> + MSM8996_SG,
> + NUM_OF_MSM8996_VERSIONS,
> +};
> +
> +static enum _msm8996_version __init qcom_cpufreq_kryo_get_msm_id(void)
> +{
> + size_t len;
> + u32 *msm_id;
> + enum _msm8996_version version;
> +
> + msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
> + /* The first 4 bytes are format, next to them is the actual msm-id */
> + msm_id++;
> +
> + switch ((enum _msm_id)*msm_id) {
> + case MSM8996V3:
> + case APQ8096V3:
> + version = MSM8996_V3;
> + break;
> + case MSM8996SG:
> + case APQ8096SG:
> + version = MSM8996_SG;
> + break;
> + default:
> + version = NUM_OF_MSM8996_VERSIONS;
> + }
> +
> + return version;
> +}
> +
> +static int __init qcom_cpufreq_kryo_driver_init(void)
> +{
> + size_t len;
> + int ret;
> + u32 versions;
> + enum _msm8996_version msm8996_version;
> + u8 *speedbin;
> + struct platform_device *pdev;
> + struct device *cpu_dev;
> + struct device_node *np;
> + struct nvmem_cell *speedbin_nvmem;
> + struct opp_table *opp_temp = NULL;
> +
> + cpu_dev = get_cpu_device(SILVER_LEAD);
> + if (IS_ERR_OR_NULL(cpu_dev))
> + return PTR_ERR(cpu_dev);
> +
> + msm8996_version = qcom_cpufreq_kryo_get_msm_id();
> + if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
> + dev_err(cpu_dev, "Not Snapdragon 820/821!");
> + return -ENODEV;
> + }
> +
> + np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
> + if (IS_ERR_OR_NULL(np))
> + return PTR_ERR(np);
> +
> + if (!of_device_is_compatible(np, "operating-points-v2-kryo-cpu")) {
> + ret = -ENOENT;
> + goto free_np;
> + }
> +
> + speedbin_nvmem = of_nvmem_cell_get(np, NULL);
> + if (IS_ERR(speedbin_nvmem)) {
> + ret = PTR_ERR(speedbin_nvmem);
> + dev_err(cpu_dev, "Could not get nvmem cell: %d\n", ret);
> + goto free_np;
> + }
> +
> + speedbin = nvmem_cell_read(speedbin_nvmem, &len);
> +
> + switch (msm8996_version) {
> + case MSM8996_V3:
> + versions = 1 << (unsigned int)(*speedbin);
> + break;
> + case MSM8996_SG:
> + versions = 1 << ((unsigned int)(*speedbin) + 4);
> + break;
> + default:
> + BUG();
> + break;
> + }
> +
> + ret = PTR_ERR_OR_ZERO(opp_temp = \
> + dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> + if (0 > ret)
> + goto free_np;
> +
> + cpu_dev = get_cpu_device(GOLD_LEAD);
> + ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> + if (0 > ret)
> + goto put_supported_hw_silver;
> +
> + of_node_put(np);
> +
> + pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
> + if (IS_ERR(pdev)) {
> + return PTR_ERR(pdev);
Don't need to free resources on error here ?
> + }
> +
> + return 0;
> +
> +put_supported_hw_silver:
> + dev_pm_opp_put_supported_hw(opp_temp);
> +
> +free_np:
> + of_node_put(np);
> +
> + return ret;
> +}
> +late_initcall(qcom_cpufreq_kryo_driver_init);
> +
> +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
--
viresh
^ permalink raw reply
* [PATCH 1/2] arm64/mm: define ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT
From: Ganesh Mahendran @ 2018-05-04 6:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180502122314.GB30246@arm.com>
2018-05-02 20:23 GMT+08:00 Will Deacon <will.deacon@arm.com>:
> On Wed, May 02, 2018 at 03:53:21PM +0800, Ganesh Mahendran wrote:
>> Set ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT for arm64. This
>> enables Speculative Page Fault handler.
>
> Are there are tests for this? I'm really nervous about enabling it...
Hi, Will
I test the arm64 spf on Qcom SDM845 cpu with kernel 4.9.
It looks good for performance, and have not found stability issue yet.
Thanks.
>
> Will
>
>>
>> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
>> ---
>> This patch is on top of Laurent's v10 spf
>> ---
>> arch/arm64/Kconfig | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index eb2cf49..cd583a9 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -144,6 +144,7 @@ config ARM64
>> select SPARSE_IRQ
>> select SYSCTL_EXCEPTION_TRACE
>> select THREAD_INFO_IN_TASK
>> + select ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT if SMP
>> help
>> ARM 64-bit (AArch64) Linux support.
>>
>> --
>> 1.9.1
>>
^ permalink raw reply
* [PATCH v4 08/14] dt: qcom: Add opp and thermal to the msm8996
From: Viresh Kumar @ 2018-05-04 6:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180402091617.GA3572@vireshk-i7>
On 2 April 2018 at 14:46, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>> + cluster0_opp: opp_table0 {
>> + compatible = "operating-points-v2";
>> + opp-shared;
>
> Is Kryo like krait where CPUs do DVFS independently ? If yes, then opp-shared
> thing should be dropped.
Have you ever replied to this question ? Sorry, but I am not able to
find it in my inbox :(
^ 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