* [2/3] serial: 8250: Add new port type for TI DA8xx/OMAPL13x/AM17xx/AM18xx
From: Franklin S Cooper Jr @ 2016-12-22 15:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482265384-715-3-git-send-email-david@lechnology.com>
On 12/20/2016 02:23 PM, David Lechner wrote:
> This adds a new UART port type for TI DA8xx/OMAPL13x/AM17xx/AM18xx. These
> SoCs have standard 8250 registers plus some extra non-standard registers.
>
> The UART will not function unless the non-standard Power and Emulation
> Management Register (PWREMU_MGMT) is configured correctly. This is
> currently handled in arch/arm/mach-davinci/serial.c for non-device-tree
> boards. Making this part of the UART driver will allow UART to work on
> device-tree boards as well and the mach code can eventually be removed.
>
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
> drivers/tty/serial/8250/8250_of.c | 1 +
> drivers/tty/serial/8250/8250_port.c | 22 ++++++++++++++++++++++
> include/uapi/linux/serial_core.h | 3 ++-
> include/uapi/linux/serial_reg.h | 8 ++++++++
> 4 files changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
> index d25ab1c..5281252 100644
> --- a/drivers/tty/serial/8250/8250_of.c
> +++ b/drivers/tty/serial/8250/8250_of.c
> @@ -332,6 +332,7 @@ static const struct of_device_id of_platform_serial_table[] = {
> .data = (void *)PORT_ALTR_16550_F128, },
> { .compatible = "mrvl,mmp-uart",
> .data = (void *)PORT_XSCALE, },
> + { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
> { /* end of list */ },
> };
> MODULE_DEVICE_TABLE(of, of_platform_serial_table);
> diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> index fe4399b..ea854054 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -273,6 +273,15 @@ static const struct serial8250_config uart_config[] = {
> .rxtrig_bytes = {1, 4, 8, 14},
> .flags = UART_CAP_FIFO,
> },
> + [PORT_DA830] = {
> + .name = "TI DA8xx/OMAPL13x/AM17xx/AM18xx",
> + .fifo_size = 16,
> + .tx_loadsz = 16,
> + .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO |
> + UART_FCR_R_TRIG_10,
> + .rxtrig_bytes = {1, 4, 8, 14},
> + .flags = UART_CAP_FIFO | UART_CAP_AFE,
> + },
> };
Any reason why the fcr and flags fields are changed when compared
against PORT_16550A?
>
> /* Uart divisor latch read */
> @@ -2118,6 +2127,19 @@ int serial8250_do_startup(struct uart_port *port)
> serial_port_out(port, UART_LCR, 0);
> }
>
> + if (port->type == PORT_DA830) {
> + /* Reset the port */
> + serial_port_out(port, UART_IER, 0);
> + serial_port_out(port, UART_DA830_PWREMU_MGMT, 0);
> + mdelay(10);
> +
> + /* Enable Tx, Rx and free run mode */
> + serial_port_out(port, UART_DA830_PWREMU_MGMT,
> + UART_DA830_PWREMU_MGMT_UTRST |
> + UART_DA830_PWREMU_MGMT_URRST |
> + UART_DA830_PWREMU_MGMT_FREE);
> + }
> +
> #ifdef CONFIG_SERIAL_8250_RSA
> /*
> * If this is an RSA port, see if we can kick it up to the
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 99dbed8..a126d05 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -56,7 +56,8 @@
> #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
> #define PORT_RT2880 29 /* Ralink RT2880 internal UART */
> #define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */
> -#define PORT_MAX_8250 30 /* max port ID */
> +#define PORT_DA830 31 /* TI DA8xx/OMAP13x/AM17xx/AM18xx */
> +#define PORT_MAX_8250 31 /* max port ID */
>
> /*
> * ARM specific type numbers. These are not currently guaranteed
> diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h
> index b4c0484..0e72eeb 100644
> --- a/include/uapi/linux/serial_reg.h
> +++ b/include/uapi/linux/serial_reg.h
> @@ -327,6 +327,14 @@
> #define SERIAL_RSA_BAUD_BASE (921600)
> #define SERIAL_RSA_BAUD_BASE_LO (SERIAL_RSA_BAUD_BASE / 8)
>
> +/* Extra registers for TI DA8xx/OMAP13x/AM17xx/AM18xx */
> +#define UART_DA830_PWREMU_MGMT 12
> +
> +/* PWREMU_MGMT register bits */
> +#define UART_DA830_PWREMU_MGMT_FREE (1 << 0) /* Free-running mode */
> +#define UART_DA830_PWREMU_MGMT_URRST (1 << 13) /* Receiver reset/enable */
> +#define UART_DA830_PWREMU_MGMT_UTRST (1 << 14) /* Transmitter reset/enable */
> +
> /*
> * Extra serial register definitions for the internal UARTs
> * in TI OMAP processors.
>
^ permalink raw reply
* [PATCH] drm: zte: support hdmi audio through spdif
From: Sean Paul @ 2016-12-22 15:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482412319-6304-1-git-send-email-shawnguo@kernel.org>
On Thu, Dec 22, 2016 at 8:11 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> From: Shawn Guo <shawn.guo@linaro.org>
>
> It enables HDMI audio support through SPDIF interface based on generic
> hdmi-audio-codec driver. The HDMI hardware supports more audio
> interfaces than SPDIF, like I2S, which may be added later.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> drivers/gpu/drm/zte/Kconfig | 1 +
> drivers/gpu/drm/zte/zx_hdmi.c | 169 +++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/zte/zx_hdmi_regs.h | 14 +++
> drivers/gpu/drm/zte/zx_vou.c | 9 ++
> drivers/gpu/drm/zte/zx_vou.h | 10 +++
> drivers/gpu/drm/zte/zx_vou_regs.h | 2 +
> 6 files changed, 205 insertions(+)
>
> diff --git a/drivers/gpu/drm/zte/Kconfig b/drivers/gpu/drm/zte/Kconfig
> index 4065b2840f1c..ed6de4b10c74 100644
> --- a/drivers/gpu/drm/zte/Kconfig
> +++ b/drivers/gpu/drm/zte/Kconfig
> @@ -4,5 +4,6 @@ config DRM_ZTE
> select DRM_KMS_CMA_HELPER
> select DRM_KMS_FB_HELPER
> select DRM_KMS_HELPER
> + select SND_SOC_HDMI_CODEC if SND_SOC
> help
> Choose this option to enable DRM on ZTE ZX SoCs.
> diff --git a/drivers/gpu/drm/zte/zx_hdmi.c b/drivers/gpu/drm/zte/zx_hdmi.c
> index 6bf6c364811e..e0b1bbfcc685 100644
> --- a/drivers/gpu/drm/zte/zx_hdmi.c
> +++ b/drivers/gpu/drm/zte/zx_hdmi.c
> @@ -25,6 +25,8 @@
> #include <drm/drm_of.h>
> #include <drm/drmP.h>
>
> +#include <sound/hdmi-codec.h>
> +
> #include "zx_hdmi_regs.h"
> #include "zx_vou.h"
>
> @@ -49,6 +51,7 @@ struct zx_hdmi {
> bool sink_is_hdmi;
> bool sink_has_audio;
> const struct vou_inf *inf;
> + struct platform_device *audio_pdev;
> };
>
> #define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x)
> @@ -366,6 +369,163 @@ static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id)
> return IRQ_NONE;
> }
>
> +static int zx_hdmi_audio_startup(struct device *dev, void *data)
> +{
> + struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> + struct drm_encoder *encoder = &hdmi->encoder;
> +
> + vou_inf_hdmi_audio_sel(encoder->crtc, VOU_HDMI_AUD_SPDIF);
> +
> + return 0;
> +}
> +
> +static void zx_hdmi_audio_shutdown(struct device *dev, void *data)
> +{
> + struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> +
> + /* Disable audio input */
> + hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, 0);
> +}
> +
> +static int zx_hdmi_audio_get_n(unsigned int fs)
> +{
> + unsigned int n;
> +
> + switch (fs) {
> + case 32000:
> + n = 4096;
> + break;
> + case 44100:
> + n = 6272;
> + break;
> + case 48000:
> + n = 6144;
> + break;
> + case 88200:
> + n = 6272 * 2;
> + break;
> + case 96000:
> + n = 6144 * 2;
> + break;
> + case 176400:
> + n = 6272 * 4;
> + break;
> + case 192000:
> + n = 6144 * 4;
> + break;
> + default:
> + n = fs * 128 / 1000;
It seems like this could be distilled down to:
if (fs && (fs % 44100) == 0)
n = 6272 * (fs / 44100);
else
n = fs * 128 / 1000;
> + }
> +
> + return n;
> +}
> +
> +static int zx_hdmi_audio_hw_params(struct device *dev,
> + void *data,
> + struct hdmi_codec_daifmt *daifmt,
> + struct hdmi_codec_params *params)
> +{
> + struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> + struct hdmi_audio_infoframe *cea = ¶ms->cea;
> + union hdmi_infoframe frame;
> + int n;
> +
> + /* We only support spdif for now */
> + if (daifmt->fmt != HDMI_SPDIF) {
> + DRM_DEV_ERROR(hdmi->dev, "invalid daifmt %d\n", daifmt->fmt);
> + return -EINVAL;
> + }
> +
> + switch (params->sample_width) {
> + case 16:
> + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
> + SPDIF_SAMPLE_SIZE_16BIT);
> + break;
> + case 20:
> + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
> + SPDIF_SAMPLE_SIZE_20BIT);
> + break;
> + case 24:
> + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
> + SPDIF_SAMPLE_SIZE_24BIT);
> + break;
> + default:
> + DRM_DEV_ERROR(hdmi->dev, "invalid sample width %d\n",
> + params->sample_width);
> + return -EINVAL;
> + }
> +
> + /* CTS is calculated by hardware, and we only need to take care of N */
> + n = zx_hdmi_audio_get_n(params->sample_rate);
> + hdmi_writeb(hdmi, N_SVAL1, n & 0xff);
> + hdmi_writeb(hdmi, N_SVAL2, (n >> 8) && 0xff);
s/&&/&/ ?
> + hdmi_writeb(hdmi, N_SVAL3, (n >> 16) & 0xf);
> +
> + /* Enable spdif mode */
> + hdmi_writeb_mask(hdmi, AUD_MODE, SPDIF_EN, SPDIF_EN);
> +
> + /* Enable audio input */
> + hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, AUD_IN_EN);
> +
> + memcpy(&frame.audio, cea, sizeof(*cea));
> +
> + return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AUDIO);
> +}
> +
> +static int zx_hdmi_audio_digital_mute(struct device *dev, void *data,
> + bool enable)
> +{
> + struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> +
> + if (enable)
> + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE,
> + TPI_AUD_MUTE);
> + else
> + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 0);
> +
> + return 0;
> +}
> +
> +static int zx_hdmi_audio_get_eld(struct device *dev, void *data,
> + uint8_t *buf, size_t len)
> +{
> + struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> + struct drm_connector *connector = &hdmi->connector;
> +
> + memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
> +
> + return 0;
> +}
> +
> +static const struct hdmi_codec_ops zx_hdmi_codec_ops = {
> + .audio_startup = zx_hdmi_audio_startup,
> + .hw_params = zx_hdmi_audio_hw_params,
> + .audio_shutdown = zx_hdmi_audio_shutdown,
> + .digital_mute = zx_hdmi_audio_digital_mute,
> + .get_eld = zx_hdmi_audio_get_eld,
> +};
> +
> +static struct hdmi_codec_pdata zx_hdmi_codec_pdata = {
> + .ops = &zx_hdmi_codec_ops,
> + .spdif = 1,
> +};
> +
> +static int zx_hdmi_audio_register(struct zx_hdmi *hdmi)
> +{
> + struct platform_device *pdev;
> +
> + pdev = platform_device_register_data(hdmi->dev, HDMI_CODEC_DRV_NAME,
> + PLATFORM_DEVID_AUTO,
> + &zx_hdmi_codec_pdata,
> + sizeof(zx_hdmi_codec_pdata));
> + if (IS_ERR(pdev))
> + return PTR_ERR(pdev);
> +
> + hdmi->audio_pdev = pdev;
> +
> + return 0;
> +}
> +
> static int zx_hdmi_i2c_read(struct zx_hdmi *hdmi, struct i2c_msg *msg)
> {
> int len = msg->len;
> @@ -566,6 +726,12 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
> return ret;
> }
>
> + ret = zx_hdmi_audio_register(hdmi);
> + if (ret) {
> + DRM_DEV_ERROR(dev, "failed to register audio: %d\n", ret);
> + return ret;
> + }
> +
> ret = zx_hdmi_register(drm, hdmi);
> if (ret) {
> DRM_DEV_ERROR(dev, "failed to register hdmi: %d\n", ret);
> @@ -590,6 +756,9 @@ static void zx_hdmi_unbind(struct device *dev, struct device *master,
>
> hdmi->connector.funcs->destroy(&hdmi->connector);
> hdmi->encoder.funcs->destroy(&hdmi->encoder);
> +
> + if (hdmi->audio_pdev)
> + platform_device_unregister(hdmi->audio_pdev);
> }
>
> static const struct component_ops zx_hdmi_component_ops = {
> diff --git a/drivers/gpu/drm/zte/zx_hdmi_regs.h b/drivers/gpu/drm/zte/zx_hdmi_regs.h
> index de911f66b658..c6d5d8211725 100644
> --- a/drivers/gpu/drm/zte/zx_hdmi_regs.h
> +++ b/drivers/gpu/drm/zte/zx_hdmi_regs.h
> @@ -52,5 +52,19 @@
> #define TPI_INFO_TRANS_RPT BIT(6)
> #define TPI_DDC_MASTER_EN 0x06f8
> #define HW_DDC_MASTER BIT(7)
> +#define N_SVAL1 0xa03
> +#define N_SVAL2 0xa04
> +#define N_SVAL3 0xa05
> +#define AUD_EN 0xa13
> +#define AUD_IN_EN BIT(0)
> +#define AUD_MODE 0xa14
> +#define SPDIF_EN BIT(1)
> +#define TPI_AUD_CONFIG 0xa62
> +#define SPDIF_SAMPLE_SIZE_SHIFT 6
> +#define SPDIF_SAMPLE_SIZE_MASK (0x3 << SPDIF_SAMPLE_SIZE_SHIFT)
> +#define SPDIF_SAMPLE_SIZE_16BIT (0x1 << SPDIF_SAMPLE_SIZE_SHIFT)
> +#define SPDIF_SAMPLE_SIZE_20BIT (0x2 << SPDIF_SAMPLE_SIZE_SHIFT)
> +#define SPDIF_SAMPLE_SIZE_24BIT (0x3 << SPDIF_SAMPLE_SIZE_SHIFT)
> +#define TPI_AUD_MUTE BIT(4)
>
> #endif /* __ZX_HDMI_REGS_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> index 8ca9c4bdeeaf..b39fbb71960a 100644
> --- a/drivers/gpu/drm/zte/zx_vou.c
> +++ b/drivers/gpu/drm/zte/zx_vou.c
> @@ -148,6 +148,15 @@ static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
> return zcrtc->vou;
> }
>
> +void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
> + enum vou_inf_hdmi_audio aud)
> +{
> + struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> + struct zx_vou_hw *vou = zcrtc->vou;
> +
> + zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud);
> +}
> +
> void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
> {
> struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
> index 1559c1f79db7..97b8f97ee3a7 100644
> --- a/drivers/gpu/drm/zte/zx_vou.h
> +++ b/drivers/gpu/drm/zte/zx_vou.h
> @@ -30,6 +30,14 @@ enum vou_inf_data_sel {
> VOU_RGB_666 = 3,
> };
>
> +enum vou_inf_hdmi_audio {
> + VOU_HDMI_AUD_SPDIF = BIT(0),
> + VOU_HDMI_AUD_I2S = BIT(1),
> + VOU_HDMI_AUD_DSD = BIT(2),
> + VOU_HDMI_AUD_HBR = BIT(3),
> + VOU_HDMI_AUD_PARALLEL = BIT(4),
> +};
> +
> struct vou_inf {
> enum vou_inf_id id;
> enum vou_inf_data_sel data_sel;
> @@ -37,6 +45,8 @@ struct vou_inf {
> u32 clocks_sel_bits;
> };
>
> +void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
> + enum vou_inf_hdmi_audio aud);
> void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc);
> void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc);
>
> diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
> index 193c1ce01fe7..48fb90bd4594 100644
> --- a/drivers/gpu/drm/zte/zx_vou_regs.h
> +++ b/drivers/gpu/drm/zte/zx_vou_regs.h
> @@ -168,6 +168,8 @@
> #define VOU_CLK_GL0_SEL BIT(4)
> #define VOU_CLK_REQEN 0x20
> #define VOU_CLK_EN 0x24
> +#define VOU_INF_HDMI_CTRL 0x30
> +#define VOU_HDMI_AUD_MASK 0x1f
>
> /* OTFPPU_CTRL registers */
> #define OTFPPU_RSZ_DATA_SOURCE 0x04
> --
> 1.9.1
>
--
Sean Paul, Software Engineer, Google / Chromium OS
^ permalink raw reply
* [PATCH 3/3] ARM: dts: sun5i: add support for Lichee Pi One board
From: Maxime Ripard @ 2016-12-22 15:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <718681482365231@web25g.yandex.ru>
On Thu, Dec 22, 2016 at 08:07:11AM +0800, Icenowy Zheng wrote:
>
>
> 22.12.2016, 06:42, "Maxime Ripard" <maxime.ripard@free-electrons.com>:
> > On Thu, Dec 22, 2016 at 04:02:35AM +0800, Icenowy Zheng wrote:
> >> ?Lichee Pi One is a low-cost Allwinner A13-based development board, with
> >> ?an AXP209 PMU, a USB2.0 OTG port, a USB2.0 host port (or an onboard
> >> ?RTL8723BU Wi-Fi card), optional headers for LCD and CSI, two GPIO
> >> ?headers and two MicroSD card slots (connected to mmc0 and mmc2, both
> >> ?bootable).
> >>
> >> ?Add support for it.
> >>
> >> ?Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> >
> > Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>
> Excuse me. Who should apply it?
Gaaah, sorry, I meant I applied it...
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161222/0d1bdac9/attachment.sig>
^ permalink raw reply
* [PATCH v2] drm: zte: add overlay plane support
From: Sean Paul @ 2016-12-22 14:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482235795-6223-1-git-send-email-shawnguo@kernel.org>
On Tue, Dec 20, 2016 at 7:09 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> From: Shawn Guo <shawn.guo@linaro.org>
>
> It enables VOU VL (Video Layer) to support overlay plane with scaling
> function. VL0 has some quirks on scaling support. We chose to skip it
> and only adds VL1 and VL2 into DRM core for now.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> Changes for v2:
> - Use clipped coordinates for overlay position calculation
>
> drivers/gpu/drm/zte/zx_plane.c | 293 ++++++++++++++++++++++++++++++++++--
> drivers/gpu/drm/zte/zx_plane_regs.h | 51 +++++++
> drivers/gpu/drm/zte/zx_vou.c | 149 +++++++++++++++++-
> drivers/gpu/drm/zte/zx_vou.h | 3 +
> drivers/gpu/drm/zte/zx_vou_regs.h | 18 +++
> 5 files changed, 497 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
> index 546eb92a94e8..8cd7cf71b2b0 100644
> --- a/drivers/gpu/drm/zte/zx_plane.c
> +++ b/drivers/gpu/drm/zte/zx_plane.c
> @@ -40,6 +40,269 @@ struct zx_plane {
> DRM_FORMAT_ARGB4444,
> };
>
> +static const uint32_t vl_formats[] = {
> + DRM_FORMAT_NV12, /* Semi-planar YUV420 */
> + DRM_FORMAT_YUV420, /* Planar YUV420 */
> + DRM_FORMAT_YUYV, /* Packed YUV422 */
> + DRM_FORMAT_YVYU,
> + DRM_FORMAT_UYVY,
> + DRM_FORMAT_VYUY,
> + DRM_FORMAT_YUV444, /* YUV444 8bit */
> + /*
> + * TODO: add formats below that HW supports:
> + * - YUV420 P010
> + * - YUV420 Hantro
> + * - YUV444 10bit
> + */
> +};
> +
> +#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
> +
> +static int zx_vl_plane_atomic_check(struct drm_plane *plane,
> + struct drm_plane_state *plane_state)
> +{
> + struct drm_framebuffer *fb = plane_state->fb;
> + struct drm_crtc *crtc = plane_state->crtc;
> + struct drm_crtc_state *crtc_state;
> + struct drm_rect clip;
> + int min_scale = FRAC_16_16(1, 8);
> + int max_scale = FRAC_16_16(8, 1);
> +
> + if (!crtc || !fb)
> + return 0;
> +
> + crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
> + crtc);
> + if (WARN_ON(!crtc_state))
> + return -EINVAL;
> +
> + /* nothing to check when disabling or disabled */
> + if (!crtc_state->enable)
> + return 0;
> +
> + /* plane must be enabled */
> + if (!plane_state->crtc)
> + return -EINVAL;
> +
> + clip.x1 = 0;
> + clip.y1 = 0;
> + clip.x2 = crtc_state->adjusted_mode.hdisplay;
> + clip.y2 = crtc_state->adjusted_mode.vdisplay;
> +
> + return drm_plane_helper_check_state(plane_state, &clip,
> + min_scale, max_scale,
> + true, true);
> +}
> +
> +static u32 zx_vl_get_fmt(uint32_t format)
> +{
> + u32 val = 0;
> +
> + switch (format) {
> + case DRM_FORMAT_NV12:
> + val = VL_FMT_YUV420;
> + break;
> + case DRM_FORMAT_YUV420:
> + val = VL_YUV420_PLANAR | VL_FMT_YUV420;
> + break;
> + case DRM_FORMAT_YUYV:
> + val = VL_YUV422_YUYV | VL_FMT_YUV422;
> + break;
> + case DRM_FORMAT_YVYU:
> + val = VL_YUV422_YVYU | VL_FMT_YUV422;
> + break;
> + case DRM_FORMAT_UYVY:
> + val = VL_YUV422_UYVY | VL_FMT_YUV422;
> + break;
> + case DRM_FORMAT_VYUY:
> + val = VL_YUV422_VYUY | VL_FMT_YUV422;
> + break;
> + case DRM_FORMAT_YUV444:
> + val = VL_FMT_YUV444_8BIT;
> + break;
> + default:
> + WARN_ONCE(1, "invalid pixel format %d\n", format);
> + }
> +
> + return val;
> +}
> +
> +static inline void zx_vl_set_update(struct zx_plane *zplane)
> +{
> + void __iomem *layer = zplane->layer;
> +
> + zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE);
> +}
> +
> +static inline void zx_vl_rsz_set_update(struct zx_plane *zplane)
> +{
> + zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1);
> +}
> +
> +static u32 zx_vl_rsz_get_fmt(uint32_t format)
> +{
> + u32 val = 0;
> +
> + switch (format) {
> + case DRM_FORMAT_NV12:
> + case DRM_FORMAT_YUV420:
> + val = RSZ_VL_FMT_YCBCR420;
> + break;
> + case DRM_FORMAT_YUYV:
> + case DRM_FORMAT_YVYU:
> + case DRM_FORMAT_UYVY:
> + case DRM_FORMAT_VYUY:
> + val = RSZ_VL_FMT_YCBCR422;
> + break;
> + case DRM_FORMAT_YUV444:
> + val = RSZ_VL_FMT_YCBCR444;
> + break;
> + default:
> + WARN_ONCE(1, "invalid pixel format %d\n", format);
> + }
> +
> + return val;
> +}
> +
> +static inline u32 rsz_step_value(u32 src, u32 dst)
> +{
> + u32 val = 0;
> +
> + if (src == dst)
> + val = 0;
> + else if (src < dst)
> + val = RSZ_PARA_STEP((src << 16) / dst);
> + else if (src > dst)
> + val = RSZ_DATA_STEP(src / dst) |
> + RSZ_PARA_STEP(((src << 16) / dst) & 0xffff);
> +
> + return val;
> +}
> +
> +static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format,
> + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h)
> +{
> + void __iomem *rsz = zplane->rsz;
> + u32 src_chroma_w = src_w;
> + u32 src_chroma_h = src_h;
> + u32 fmt;
> +
> + /* Set up source and destination resolution */
> + zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
> + zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
> +
> + /* Configure data format for VL RSZ */
> + fmt = zx_vl_rsz_get_fmt(format);
> + zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt);
> +
> + /* Calculate Chroma heigth and width */
> + if (fmt == RSZ_VL_FMT_YCBCR420) {
> + src_chroma_w = src_w >> 1;
> + src_chroma_h = src_h >> 1;
> + } else if (fmt == RSZ_VL_FMT_YCBCR422) {
> + src_chroma_w = src_w >> 1;
> + }
> +
> + /* Set up Luma and Chroma step registers */
> + zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w));
> + zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h));
> + zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w));
> + zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h));
> +
> + zx_vl_rsz_set_update(zplane);
> +}
> +
> +static void zx_vl_plane_atomic_update(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct zx_plane *zplane = to_zx_plane(plane);
> + struct drm_plane_state *state = plane->state;
> + struct drm_framebuffer *fb = state->fb;
> + struct drm_rect *src = &state->src;
> + struct drm_rect *dst = &state->dst;
> + struct drm_gem_cma_object *cma_obj;
> + void __iomem *layer = zplane->layer;
> + void __iomem *hbsc = zplane->hbsc;
> + void __iomem *paddr_reg;
> + dma_addr_t paddr;
> + u32 src_x, src_y, src_w, src_h;
> + u32 dst_x, dst_y, dst_w, dst_h;
> + uint32_t format;
> + u32 fmt;
> + int num_planes;
> + int i;
> +
> + if (!fb)
> + return;
> +
> + format = fb->pixel_format;
> +
> + src_x = src->x1 >> 16;
> + src_y = src->y1 >> 16;
> + src_w = drm_rect_width(src) >> 16;
> + src_h = drm_rect_height(src) >> 16;
> +
> + dst_x = dst->x1;
> + dst_y = dst->y1;
> + dst_w = drm_rect_width(dst);
> + dst_h = drm_rect_height(dst);
> +
> + /* Set up data address registers for Y, Cb and Cr planes */
> + num_planes = drm_format_num_planes(format);
> + paddr_reg = layer + VL_Y;
> + for (i = 0; i < num_planes; i++) {
> + cma_obj = drm_fb_cma_get_gem_obj(fb, i);
> + paddr = cma_obj->paddr + fb->offsets[i];
> + paddr += src_y * fb->pitches[i];
> + paddr += src_x * drm_format_plane_cpp(format, i);
> + zx_writel(paddr_reg, paddr);
> + paddr_reg += 4;
> + }
> +
> + /* Set up source height/width register */
> + zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
> +
> + /* Set up start position register */
> + zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
> +
> + /* Set up end position register */
> + zx_writel(layer + VL_POS_END,
> + GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
> +
> + /* Strides of Cb and Cr planes should be identical */
> + zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) |
> + CHROMA_STRIDE(fb->pitches[1]));
> +
> + /* Set up video layer data format */
> + fmt = zx_vl_get_fmt(format);
> + zx_writel(layer + VL_CTRL1, fmt);
> +
> + /* Always use scaler since it exists (set for not bypass) */
> + zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE,
> + VL_SCALER_BYPASS_MODE);
> +
> + zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h);
> +
> + /* Enable HBSC block */
> + zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
> +
> + zx_overlay_enable(plane);
> +
> + zx_vl_set_update(zplane);
> +}
> +
> +static void zx_vl_plane_atomic_disable(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + zx_overlay_disable(plane);
> +}
> +
> +static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = {
> + .atomic_check = zx_vl_plane_atomic_check,
> + .atomic_update = zx_vl_plane_atomic_update,
> + .atomic_disable = zx_vl_plane_atomic_disable,
> +};
> +
> static int zx_gl_plane_atomic_check(struct drm_plane *plane,
> struct drm_plane_state *plane_state)
> {
> @@ -107,14 +370,6 @@ static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
> zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1);
> }
>
> -void zx_plane_set_update(struct drm_plane *plane)
> -{
> - struct zx_plane *zplane = to_zx_plane(plane);
> -
> - zx_gl_rsz_set_update(zplane);
> - zx_gl_set_update(zplane);
> -}
> -
> static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
> u32 dst_w, u32 dst_h)
> {
> @@ -230,6 +485,24 @@ static void zx_plane_destroy(struct drm_plane *plane)
> .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> };
>
> +void zx_plane_set_update(struct drm_plane *plane)
> +{
> + struct zx_plane *zplane = to_zx_plane(plane);
> +
> + switch (plane->type) {
> + case DRM_PLANE_TYPE_PRIMARY:
> + zx_gl_rsz_set_update(zplane);
> + zx_gl_set_update(zplane);
> + break;
> + case DRM_PLANE_TYPE_OVERLAY:
> + zx_vl_rsz_set_update(zplane);
> + zx_vl_set_update(zplane);
> + break;
> + default:
> + WARN_ONCE(1, "unsupported plane type %d\n", plane->type);
> + }
> +}
> +
> static void zx_plane_hbsc_init(struct zx_plane *zplane)
> {
> void __iomem *hbsc = zplane->hbsc;
> @@ -279,7 +552,9 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
> format_count = ARRAY_SIZE(gl_formats);
> break;
> case DRM_PLANE_TYPE_OVERLAY:
> - /* TODO: add video layer (vl) support */
> + helper = &zx_vl_plane_helper_funcs;
> + formats = vl_formats;
> + format_count = ARRAY_SIZE(vl_formats);
> break;
> default:
> return ERR_PTR(-ENODEV);
> diff --git a/drivers/gpu/drm/zte/zx_plane_regs.h b/drivers/gpu/drm/zte/zx_plane_regs.h
> index 3dde6716a558..65f271aeabed 100644
> --- a/drivers/gpu/drm/zte/zx_plane_regs.h
> +++ b/drivers/gpu/drm/zte/zx_plane_regs.h
> @@ -46,6 +46,37 @@
> #define GL_POS_X(x) (((x) << GL_POS_X_SHIFT) & GL_POS_X_MASK)
> #define GL_POS_Y(x) (((x) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK)
>
> +/* VL registers */
> +#define VL_CTRL0 0x00
> +#define VL_UPDATE BIT(3)
> +#define VL_CTRL1 0x04
> +#define VL_YUV420_PLANAR BIT(5)
> +#define VL_YUV422_SHIFT 3
> +#define VL_YUV422_YUYV (0 << VL_YUV422_SHIFT)
> +#define VL_YUV422_YVYU (1 << VL_YUV422_SHIFT)
> +#define VL_YUV422_UYVY (2 << VL_YUV422_SHIFT)
> +#define VL_YUV422_VYUY (3 << VL_YUV422_SHIFT)
> +#define VL_FMT_YUV420 0
> +#define VL_FMT_YUV422 1
> +#define VL_FMT_YUV420_P010 2
> +#define VL_FMT_YUV420_HANTRO 3
> +#define VL_FMT_YUV444_8BIT 4
> +#define VL_FMT_YUV444_10BIT 5
> +#define VL_CTRL2 0x08
> +#define VL_SCALER_BYPASS_MODE BIT(0)
> +#define VL_STRIDE 0x0c
> +#define LUMA_STRIDE_SHIFT 16
> +#define LUMA_STRIDE_MASK (0xffff << LUMA_STRIDE_SHIFT)
> +#define CHROMA_STRIDE_SHIFT 0
> +#define CHROMA_STRIDE_MASK (0xffff << CHROMA_STRIDE_SHIFT)
> +#define VL_SRC_SIZE 0x10
> +#define VL_Y 0x14
> +#define VL_POS_START 0x30
> +#define VL_POS_END 0x34
> +
> +#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
> +#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
> +
> /* CSC registers */
> #define CSC_CTRL0 0x30
> #define CSC_COV_MODE_SHIFT 16
> @@ -69,6 +100,18 @@
> #define RSZ_DEST_CFG 0x04
> #define RSZ_ENABLE_CFG 0x14
>
> +#define RSZ_VL_LUMA_HOR 0x08
> +#define RSZ_VL_LUMA_VER 0x0c
> +#define RSZ_VL_CHROMA_HOR 0x10
> +#define RSZ_VL_CHROMA_VER 0x14
> +#define RSZ_VL_CTRL_CFG 0x18
> +#define RSZ_VL_FMT_SHIFT 3
> +#define RSZ_VL_FMT_MASK (0x3 << RSZ_VL_FMT_SHIFT)
> +#define RSZ_VL_FMT_YCBCR420 (0x0 << RSZ_VL_FMT_SHIFT)
> +#define RSZ_VL_FMT_YCBCR422 (0x1 << RSZ_VL_FMT_SHIFT)
> +#define RSZ_VL_FMT_YCBCR444 (0x2 << RSZ_VL_FMT_SHIFT)
> +#define RSZ_VL_ENABLE_CFG 0x1c
> +
> #define RSZ_VER_SHIFT 16
> #define RSZ_VER_MASK (0xffff << RSZ_VER_SHIFT)
> #define RSZ_HOR_SHIFT 0
> @@ -77,6 +120,14 @@
> #define RSZ_VER(x) (((x) << RSZ_VER_SHIFT) & RSZ_VER_MASK)
> #define RSZ_HOR(x) (((x) << RSZ_HOR_SHIFT) & RSZ_HOR_MASK)
>
> +#define RSZ_DATA_STEP_SHIFT 16
> +#define RSZ_DATA_STEP_MASK (0xffff << RSZ_DATA_STEP_SHIFT)
> +#define RSZ_PARA_STEP_SHIFT 0
> +#define RSZ_PARA_STEP_MASK (0xffff << RSZ_PARA_STEP_SHIFT)
> +
> +#define RSZ_DATA_STEP(x) (((x) << RSZ_DATA_STEP_SHIFT) & RSZ_DATA_STEP_MASK)
> +#define RSZ_PARA_STEP(x) (((x) << RSZ_PARA_STEP_SHIFT) & RSZ_PARA_STEP_MASK)
> +
> /* HBSC registers */
> #define HBSC_SATURATION 0x00
> #define HBSC_HUE 0x04
> diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> index 73fe15c17c32..8ca9c4bdeeaf 100644
> --- a/drivers/gpu/drm/zte/zx_vou.c
> +++ b/drivers/gpu/drm/zte/zx_vou.c
> @@ -93,10 +93,38 @@ struct zx_crtc {
> const struct zx_crtc_bits *bits;
> enum vou_chn_type chn_type;
> struct clk *pixclk;
> + u32 overlay_bitmap;
> };
>
> #define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
>
> +struct zx_vl_bits {
> + u32 enable;
> + u32 chnsel;
> + u32 clksel;
> +};
> +
> +static const struct zx_vl_bits zx_vl_bits[VL_NUM] = {
> + {
> + .enable = OSD_CTRL0_VL0_EN,
> + .chnsel = OSD_CTRL0_VL0_SEL,
> + .clksel = VOU_CLK_VL0_SEL,
> + }, {
> + .enable = OSD_CTRL0_VL1_EN,
> + .chnsel = OSD_CTRL0_VL1_SEL,
> + .clksel = VOU_CLK_VL1_SEL,
> + }, {
> + .enable = OSD_CTRL0_VL2_EN,
> + .chnsel = OSD_CTRL0_VL2_SEL,
> + .clksel = VOU_CLK_VL2_SEL,
> + },
> +};
> +
> +struct zx_overlay {
> + struct drm_plane *plane;
If you subclass plane instead of storing the pointer, you don't need
to keep an array of overlays in vou_hw or the find_vl_idx function.
> + const struct zx_vl_bits *bits;
> +};
> +
> struct zx_vou_hw {
> struct device *dev;
> void __iomem *osd;
> @@ -110,6 +138,7 @@ struct zx_vou_hw {
> struct clk *aux_clk;
> struct zx_crtc *main_crtc;
> struct zx_crtc *aux_crtc;
> + struct zx_overlay overlays[VL_NUM];
> };
>
> static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
> @@ -404,6 +433,112 @@ void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe)
> zcrtc->bits->int_frame_mask, 0);
> }
>
> +static int zx_overlay_find_vl_idx(struct drm_plane *plane,
> + struct zx_vou_hw *vou)
> +{
> + int i;
> +
> + for (i = 0; i < VL_NUM; i++) {
> + if (vou->overlays[i].plane == plane)
> + break;
> + }
> +
> + if (i == VL_NUM) {
> + DRM_DEV_ERROR(vou->dev, "failed to find VL\n");
> + return -EINVAL;
> + }
> +
> + return i;
> +}
> +
> +void zx_overlay_enable(struct drm_plane *plane)
> +{
> + struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc);
> + struct zx_vou_hw *vou = zcrtc->vou;
> + const struct zx_vl_bits *bits;
> + int idx;
> +
> + idx = zx_overlay_find_vl_idx(plane, vou);
> + if (idx < 0)
> + return;
> +
> + bits = vou->overlays[idx].bits;
> + zcrtc->overlay_bitmap |= 1 << idx;
> +
> + if (zcrtc->chn_type == VOU_CHN_MAIN) {
> + zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0);
> + zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0);
> + } else {
> + zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel,
> + bits->chnsel);
> + zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel,
> + bits->clksel);
> + }
> +
> + zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
> +}
> +
> +void zx_overlay_disable(struct drm_plane *plane)
> +{
> + struct zx_crtc *zcrtc = to_zx_crtc(plane->crtc);
> + struct zx_vou_hw *vou = zcrtc->vou;
> + const struct zx_vl_bits *bits;
> + int idx;
> +
> + idx = zx_overlay_find_vl_idx(plane, vou);
> + if (idx < 0)
> + return;
> +
> + bits = vou->overlays[idx].bits;
> + zcrtc->overlay_bitmap &= ~(1 << idx);
> +
> + zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
> +}
> +
> +static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou)
> +{
> + struct device *dev = vou->dev;
> + struct drm_plane *plane;
> + struct zx_layer_data data;
> + int i;
> +
> + /*
> + * VL0 has some quirks on scaling support which need special handling.
> + * Let's leave it out for now.
> + */
> + for (i = 1; i < VL_NUM; i++) {
> + data.layer = vou->osd + OSD_VL_OFFSET(i);
> + data.hbsc = vou->osd + HBSC_VL_OFFSET(i);
> + data.rsz = vou->otfppu + RSZ_VL_OFFSET(i);
> +
> + plane = zx_plane_init(drm, dev, &data, DRM_PLANE_TYPE_OVERLAY);
> + if (IS_ERR(plane)) {
> + DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i);
> + continue;
> + }
> +
> + vou->overlays[i].plane = plane;
> + vou->overlays[i].bits = &zx_vl_bits[i];
> + }
> +}
> +
> +static inline void zx_osd_int_update(struct zx_crtc *zcrtc)
> +{
> + u32 bitmap = zcrtc->overlay_bitmap;
> + int i;
> +
> + vou_chn_set_update(zcrtc);
> + zx_plane_set_update(zcrtc->primary);
> +
> + if (bitmap) {
> + for (i = 0; i < VL_NUM; i++) {
> + if ((bitmap & (1 << i)) == 0)
> + continue;
> + zx_plane_set_update(zcrtc->vou->overlays[i].plane);
> + }
> + }
> +}
> +
> static irqreturn_t vou_irq_handler(int irq, void *dev_id)
> {
> struct zx_vou_hw *vou = dev_id;
> @@ -423,15 +558,11 @@ static irqreturn_t vou_irq_handler(int irq, void *dev_id)
> state = zx_readl(vou->osd + OSD_INT_STA);
> zx_writel(vou->osd + OSD_INT_CLRSTA, state);
>
> - if (state & OSD_INT_MAIN_UPT) {
> - vou_chn_set_update(vou->main_crtc);
> - zx_plane_set_update(vou->main_crtc->primary);
> - }
> + if (state & OSD_INT_MAIN_UPT)
> + zx_osd_int_update(vou->main_crtc);
>
> - if (state & OSD_INT_AUX_UPT) {
> - vou_chn_set_update(vou->aux_crtc);
> - zx_plane_set_update(vou->aux_crtc->primary);
> - }
> + if (state & OSD_INT_AUX_UPT)
> + zx_osd_int_update(vou->aux_crtc);
>
> if (state & OSD_INT_ERROR)
> DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
> @@ -611,6 +742,8 @@ static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
> goto disable_ppu_clk;
> }
>
> + zx_overlay_init(drm, vou);
> +
> return 0;
>
> disable_ppu_clk:
> diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
> index 349e06cd86f4..1559c1f79db7 100644
> --- a/drivers/gpu/drm/zte/zx_vou.h
> +++ b/drivers/gpu/drm/zte/zx_vou.h
> @@ -43,4 +43,7 @@ struct vou_inf {
> int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe);
> void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe);
>
> +void zx_overlay_enable(struct drm_plane *plane);
> +void zx_overlay_disable(struct drm_plane *plane);
> +
> #endif /* __ZX_VOU_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
> index f44e7a4ae441..193c1ce01fe7 100644
> --- a/drivers/gpu/drm/zte/zx_vou_regs.h
> +++ b/drivers/gpu/drm/zte/zx_vou_regs.h
> @@ -22,6 +22,15 @@
> #define AUX_HBSC_OFFSET 0x860
> #define AUX_RSZ_OFFSET 0x800
>
> +#define OSD_VL0_OFFSET 0x040
> +#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i))
> +
> +#define HBSC_VL0_OFFSET 0x760
> +#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i))
> +
> +#define RSZ_VL1_U0 0xa00
> +#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i))
> +
> /* OSD (GPC_GLOBAL) registers */
> #define OSD_INT_STA 0x04
> #define OSD_INT_CLRSTA 0x08
> @@ -42,6 +51,12 @@
> )
> #define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
> #define OSD_CTRL0 0x10
> +#define OSD_CTRL0_VL0_EN BIT(13)
> +#define OSD_CTRL0_VL0_SEL BIT(12)
> +#define OSD_CTRL0_VL1_EN BIT(11)
> +#define OSD_CTRL0_VL1_SEL BIT(10)
> +#define OSD_CTRL0_VL2_EN BIT(9)
> +#define OSD_CTRL0_VL2_SEL BIT(8)
> #define OSD_CTRL0_GL0_EN BIT(7)
> #define OSD_CTRL0_GL0_SEL BIT(6)
> #define OSD_CTRL0_GL1_EN BIT(5)
> @@ -146,6 +161,9 @@
> #define VOU_INF_DATA_SEL 0x08
> #define VOU_SOFT_RST 0x14
> #define VOU_CLK_SEL 0x18
> +#define VOU_CLK_VL2_SEL BIT(8)
> +#define VOU_CLK_VL1_SEL BIT(7)
> +#define VOU_CLK_VL0_SEL BIT(6)
> #define VOU_CLK_GL1_SEL BIT(5)
> #define VOU_CLK_GL0_SEL BIT(4)
> #define VOU_CLK_REQEN 0x20
> --
> 1.9.1
>
--
Sean Paul, Software Engineer, Google / Chromium OS
^ permalink raw reply
* [PATCH] mm: pmd dirty emulation in page fault handler
From: Minchan Kim @ 2016-12-22 14:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161222081713.GA32480@node.shutemov.name>
Hello,
On Thu, Dec 22, 2016 at 11:17:13AM +0300, Kirill A. Shutemov wrote:
< snip >
> > diff --git a/mm/memory.c b/mm/memory.c
> > index 36c774f..7408ddc 100644
> > --- a/mm/memory.c
> > +++ b/mm/memory.c
> > @@ -3637,18 +3637,20 @@ static int __handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
> > if (pmd_protnone(orig_pmd) && vma_is_accessible(vma))
> > return do_huge_pmd_numa_page(&vmf, orig_pmd);
> >
> > - if ((vmf.flags & FAULT_FLAG_WRITE) &&
> > - !pmd_write(orig_pmd)) {
> > - ret = wp_huge_pmd(&vmf, orig_pmd);
> > - if (!(ret & VM_FAULT_FALLBACK))
> > + if (vmf.flags & FAULT_FLAG_WRITE) {
> > + if (!pmd_write(orig_pmd)) {
> > + ret = wp_huge_pmd(&vmf, orig_pmd);
> > + if (ret == VM_FAULT_FALLBACK)
>
> In theory, more than one flag can be set and it would lead to
> false-negative. Bit check was the right thing.
>
> And I don't understand why do you need to change code in
> __handle_mm_fault() at all.
> From what I see change to huge_pmd_set_accessed() should be enough.
Yeb. Thanks for the review. Here v2 goes.
>From b3ec95c0df91ad113525968a4a6b53030fd0b48d Mon Sep 17 00:00:00 2001
From: Minchan Kim <minchan@kernel.org>
Date: Thu, 22 Dec 2016 23:43:49 +0900
Subject: [PATCH v2] mm: pmd dirty emulation in page fault handler
Andreas reported [1] made a test in jemalloc hang in THP mode in arm64.
http://lkml.kernel.org/r/mvmmvfy37g1.fsf at hawking.suse.de
The problem is page fault handler supports only accessed flag emulation
for THP page of SW-dirty/accessed architecture.
This patch enables dirty-bit emulation for those architectures.
Without it, MADV_FREE makes application hang by repeated fault forever.
[1] b8d3c4c3009d, mm/huge_memory.c: don't split THP page when MADV_FREE syscall is called
Cc: Jason Evans <je@fb.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: linux-arch at vger.kernel.org
Cc: linux-arm-kernel at lists.infradead.org
Cc: <stable@vger.kernel.org> [4.5+]
Fixes: b8d3c4c3009d ("mm/huge_memory.c: don't split THP page when MADV_FREE syscall is called")
Reported-by: Andreas Schwab <schwab@suse.de>
Signed-off-by: Minchan Kim <minchan@kernel.org>
---
* from v1
* Remove __handle_mm_fault part - Kirill
mm/huge_memory.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 10eedbf..29ec8a4 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -883,15 +883,17 @@ void huge_pmd_set_accessed(struct vm_fault *vmf, pmd_t orig_pmd)
{
pmd_t entry;
unsigned long haddr;
+ bool write = vmf->flags & FAULT_FLAG_WRITE;
vmf->ptl = pmd_lock(vmf->vma->vm_mm, vmf->pmd);
if (unlikely(!pmd_same(*vmf->pmd, orig_pmd)))
goto unlock;
entry = pmd_mkyoung(orig_pmd);
+ if (write)
+ entry = pmd_mkdirty(entry);
haddr = vmf->address & HPAGE_PMD_MASK;
- if (pmdp_set_access_flags(vmf->vma, haddr, vmf->pmd, entry,
- vmf->flags & FAULT_FLAG_WRITE))
+ if (pmdp_set_access_flags(vmf->vma, haddr, vmf->pmd, entry, write))
update_mmu_cache_pmd(vmf->vma, vmf->address, vmf->pmd);
unlock:
--
2.7.4
^ permalink raw reply related
* [RFC PATCH v3 2/2] drm/panel: Add support for Chunghwa CLAA070WP03XG panel
From: ayaka @ 2016-12-22 14:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161207145555.kdg7azcrpnwnb2r6@phenom.ffwll.local>
On 12/07/2016 10:55 PM, Daniel Vetter wrote:
> On Wed, Dec 07, 2016 at 08:57:23AM +0800, Ayaka wrote:
>>
>> ??? iPad ??
>>
>>> Thierry Reding <thierry.reding@gmail.com> ? 2016?12?6? ??11:46 ???
>>>
>>>> On Tue, Sep 20, 2016 at 03:02:51AM +0800, Randy Li wrote:
>>>> The Chunghwa CLAA070WP03XG is a 7" 1280x800 panel, which can be
>>>> supported by the simple panel driver.
>>>>
>>>> Signed-off-by: Randy Li <ayaka@soulik.info>
>>>> ---
>>>> .../display/panel/chunghwa,claa070wp03xg.txt | 7 ++++++
>>>> drivers/gpu/drm/panel/panel-simple.c | 27 ++++++++++++++++++++++
>>>> 2 files changed, 34 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/display/panel/chunghwa,claa070wp03xg.txt
>>> Applied, thanks.
>> Wait, it is RFC, not pass the test.
> Well 2 months of silence, it's reasonable to assume that this works for
> you ... I guess you need to supply a fixup patch asap ;-)
Sorry, my exynos 4412 board is broken, I will order one and fix this
problem, please don't merge this patch until somebody confirm that it is
adjusted to correct parameter.
> -Daniel
^ permalink raw reply
* [PATCH] ARM: dts: imx: Pass an empty 'chosen' node
From: Pali Rohár @ 2016-12-22 14:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f54f8c27-1745-ac30-4eb4-35e075877c08@osg.samsung.com>
Hi!
On Monday 19 December 2016 22:44:40 Javier Martinez Canillas wrote:
> [adding Pali who first reported this issue]
>
> Hello Pali,
>
> On 12/19/2016 06:30 PM, Fabio Estevam wrote:
> > Hi Uwe,
> >
> > On Mon, Dec 19, 2016 at 4:41 PM, Uwe Kleine-K?nig
> >
> > <u.kleine-koenig@pengutronix.de> wrote:
> >> wouldn't it be better to fix the decompressor code to eventually
> >> create the /chosen node when it doesn't exist?
> >
> > Thanks for the suggestion.
> >
> > I don't have a Barebox setup handy, but would the following change
> > fix it as you propose?
>
> Could you test Fabio's patch with NoLo to see if it fixes your issue?
>
> I also think it makes more sense for Linux to create the "chosen"
> node if not present since this is a requirement due a Linux
> implementation detail.
>
> Forcing to have a "chosen" node in dts leaks this implementation
> detail since the ePAPR document says that the "chosen" node should
> be optional.
>
> > --- a/arch/arm/boot/compressed/atags_to_fdt.c
> > +++ b/arch/arm/boot/compressed/atags_to_fdt.c
> > @@ -66,13 +66,21 @@ static uint32_t get_cell_size(const void *fdt)
> >
> > return cell_size;
> >
> > }
> >
> > -static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
> > +static int merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
> >
> > {
> >
> > char cmdline[COMMAND_LINE_SIZE];
> > const char *fdt_bootargs;
> > char *ptr = cmdline;
> >
> > + int chosen_off;
> >
> > int len = 0;
> >
> > + /* find or add chosen node */
> > + chosen_off = fdt_path_offset(fdt, "/chosen");
> > + if (chosen_off == -FDT_ERR_NOTFOUND)
> > + chosen_off = fdt_add_subnode(fdt, 0, "chosen");
> > + if (chosen_off < 0)
> > + return chosen_off;
> > +
> >
> > /* copy the fdt command line into the buffer */
> > fdt_bootargs = getprop(fdt, "/chosen", "bootargs", &len);
> > if (fdt_bootargs)
This patch looks wrong. The next if condition is there to read
/chosen/boootargs entry because this function merge_fdt_bootargs() is
merging them together. And for reading bootargs you do not need to
create /chosen node, if it does not exists above getprop() returns NULL
and next if correctly handles it (= no bootargs for merging).
But setting /chosen/bootargs is also outside of this function in
atags_to_fdt() where merge_fdt_bootargs() does not have to be called (in
case do_extend_cmdline is not truth).
So this patch does not fix real problem, 'chosen' node does not have to
exist even after applying this patch.
I'm for fixing this uncompressor code so 'chosen' stay optional (as
written in that ePAPR document) and Linux kernel uncompressor correctly
creates it. But this patch is not doing it (correctly).
--
Pali Roh?r
pali.rohar at gmail.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161222/ebc24098/attachment-0001.sig>
^ permalink raw reply
* [RESEND PATCH v2] arm64: dts: rockchip: add u2phy clock for ehci and ohci of rk3399
From: wlf @ 2016-12-22 13:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAD=FV=WFFGSc7yBeaE+++VAuRKwMixpGUwA9bCCro4TCe0+GAA@mail.gmail.com>
Dear Doug & Xing Zheng,
? 2016?12?22? 08:47, Doug Anderson ??:
> Hi,
>
> On Wed, Dec 21, 2016 at 2:41 AM, Xing Zheng <zhengxing@rock-chips.com> wrote:
>> From: William wu <wulf@rock-chips.com>
>>
>> We found that the suspend process was blocked when it run into
>> ehci/ohci module due to clk-480m of usb2-phy was disabled.
>>
>> The root cause is that usb2-phy suspended earlier than ehci/ohci
>> (usb2-phy will be auto suspended if no devices plug-in). and the
>> clk-480m provided by it was disabled if no module used. However,
I suggest that change the "clk-480m" to "utmi clock" here.
>> some suspend process related ehci/ohci are base on this clock,
>> so we should refer it into ehci/ohci driver to prevent this case.
>>
>> The u2phy clock flow like this:
>> ===
>> u2phy ________________
>> | | |-----> UTMI_CLK ---------> | EHCI |
>> OSC_24M ---|---> PHY_PLL----|----|
>> |________^_______| |-----> 480M_CLK ---|G|---> | USBPHY_480M_SRC| ----> USBPHY_480M for SoC
>> |
>> |
>> GRF
>> ===
>>
>> Signed-off-by: William wu <wulf@rock-chips.com>
>> Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
>> ---
>>
>> Changes in v2:
>> - update the commit message
>> - remove patches whic add and export the USBPHYx_480M_SRC clock IDs
>>
>> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 28 ++++++++++++++++++++--------
>> 1 file changed, 20 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> index b65c193..2ad9255 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -315,8 +315,10 @@
>> compatible = "generic-ehci";
>> reg = <0x0 0xfe380000 0x0 0x20000>;
>> interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH 0>;
>> - clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
>> - clock-names = "hclk_host0", "hclk_host0_arb";
>> + clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
>> + <&u2phy0>;
>> + clock-names = "usbhost", "arbiter",
>> + "utmi";
>> phys = <&u2phy0_host>;
>> phy-names = "usb";
>> status = "disabled";
>> @@ -326,8 +328,12 @@
>> compatible = "generic-ohci";
>> reg = <0x0 0xfe3a0000 0x0 0x20000>;
>> interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH 0>;
>> - clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
>> - clock-names = "hclk_host0", "hclk_host0_arb";
>> + clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
>> + <&u2phy0>;
>> + clock-names = "usbhost", "arbiter",
>> + "utmi";
>> + phys = <&u2phy0_host>;
>> + phy-names = "usb";
>> status = "disabled";
>> };
>>
>> @@ -335,8 +341,10 @@
>> compatible = "generic-ehci";
>> reg = <0x0 0xfe3c0000 0x0 0x20000>;
>> interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH 0>;
>> - clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
>> - clock-names = "hclk_host1", "hclk_host1_arb";
>> + clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
>> + <&u2phy1>;
>> + clock-names = "usbhost", "arbiter",
>> + "utmi";
>> phys = <&u2phy1_host>;
>> phy-names = "usb";
>> status = "disabled";
>> @@ -346,8 +354,12 @@
>> compatible = "generic-ohci";
>> reg = <0x0 0xfe3e0000 0x0 0x20000>;
>> interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH 0>;
>> - clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
>> - clock-names = "hclk_host1", "hclk_host1_arb";
>> + clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
>> + <&u2phy1>;
>> + clock-names = "usbhost", "arbiter",
>> + "utmi";
>> + phys = <&u2phy1_host>;
>> + phy-names = "usb";
> This all looks better to me. From a device tree point of view it
> makes lots of sense to expose this PHY clock to the controller. Thus:
>
> Reviewed-by: Douglas Anderson <dianders@chromium.org>
>
>
> I can't say that I understand all the interactions between the PHY
> code and the USB driver, but presumably others have reviewed that
> more? Offline Heiko pointed me at rockchip_usb2phy_otg_sm_work()
> which apparently calls rockchip_usb2phy_power_off() and
> rockchip_usb2phy_power_on() directly sometimes behind the back of the
> PHY framework. Very strange.
Yes, the rockchip_usb2phy_otg_sm_work() in the USB2 PHY driver and
EHCI/OHCI platform
driver via the PHY framework can both do phy power on/off. We do that
because we want
to reduce USB2 PHY consumption in system runtime and deep sleep.
For EHCI/OHCI platform drivers, they call phy_power_on() in probe to
power on USB2 PHY,
and don't power off USB2 PHY in system runtime, even if there's no USB2
device connected.
They just call phy_power_off() in PM suspend, and call phy_power_on() in
PM resume to
power on USB2 PHY again. AKA the EHCI/OHCI platform driver can only
support USB power
management in deep sleep.
So we add a work in USB2 PHY driver to manage USB2 PHY power. The work
scheduled every
60 seconds, check the USB D+/D- and disconnect status in GRF registers
to know if any USB
device connected. If no USB connected, it will call
rockchip_usb2phy_power_off() to power
off USB2 PHY. And also, the USB2 PHY driver use an irq to detect D+/D-
voltage when USB
device connected? and then call rockchip_usb2phy_power_on() to power on
USB2 PHY.
AKA the work is used to support USB PHY power management in runtime.
>
>
> I will also say that there were still some unanswered questions from
> the previous thread, namely:
>
> A) Heiko: Also, with the change, the ehci will keep the clock (and
> thus the phy) always on. Does the phy-autosuspend even save anything
> now?
With this patch, the GRF USB commonon will always enable in system
runtime. But we still
can set USB2 PHY enter suspend mode by power down most of PHY logic
module via GRF,
so the phy-autosuspend is still useful.
On the other hand, commonon bit is used for PLL clock logical, its power
consumption is
not big in theory, I remember that I tested on the other SoC? the
commonon bit consumed
about 1~2mA.
> B) Brian: Is thre a race between power_off() and the delayed work in
> your USB2 PHY driver?
I'm not clear understand the race mentioned here.
IMO, PHY framework power_off() and work rockchip_usb2phy_power_off()
don't have race
problem.
And maybe there is a race between PHY framework power_off() and the
delayed work
rockchip_usb2phy_power_on() in USB2 PHY driver, but I think the
probability is low?
the race case maybe?
1. EHCI/OHCI platform call phy_power_off() in PM suspend;
2. Plug in an USB device after USB2 PHY has entered suspend;
3. Power on USB2 PHY again in work;
4. After these, finally the USB2 PHY is power on when system enter sleep.
>
>
> IMHO neither of those two questions affect the correctness of this
> patch: that this clock ought to be provided to the USB Controller.
> ...but they both are important questions that should be answered.
>
> One other last note is that we probably should be specifying a more
> specific compatible string, like:
>
> "rk3399-ehci", "generic-ehci"
>
> That will allow us later to use these same device tree files and
> perhaps deal with the clocks / PHYs in a more efficient way.
>
>
> -Doug
>
>
>
^ permalink raw reply
* [PATCH v7 5/5] ARM: configs: stm32: Add I2C support for STM32 defconfig
From: M'boumba Cedric Madianga @ 2016-12-22 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482413704-17531-1-git-send-email-cedric.madianga@gmail.com>
This patch adds I2C support for STM32 default configuration
Signed-off-by: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
---
arch/arm/configs/stm32_defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index e7b56d4..9494eaf 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -52,6 +52,9 @@ CONFIG_SERIAL_NONSTANDARD=y
CONFIG_SERIAL_STM32=y
CONFIG_SERIAL_STM32_CONSOLE=y
# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_STM32F4=y
# CONFIG_HWMON is not set
# CONFIG_USB_SUPPORT is not set
CONFIG_NEW_LEDS=y
--
1.9.1
^ permalink raw reply related
* [PATCH v7 4/5] ARM: dts: stm32: Add I2C1 support for STM32429 eval board
From: M'boumba Cedric Madianga @ 2016-12-22 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482413704-17531-1-git-send-email-cedric.madianga@gmail.com>
This patch adds I2C1 instance support for STM32x9I-Eval board.
Signed-off-by: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index afb90bc..74e0045 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -141,3 +141,9 @@
pinctrl-names = "default";
status = "okay";
};
+
+&i2c1 {
+ pinctrl-0 = <&i2c1_pins_b>;
+ pinctrl-names = "default";
+ status = "okay";
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v7 3/5] ARM: dts: stm32: Add I2C1 support for STM32F429 SoC
From: M'boumba Cedric Madianga @ 2016-12-22 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482413704-17531-1-git-send-email-cedric.madianga@gmail.com>
This patch adds I2C1 support for STM32F429 SoC
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Signed-off-by: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 7de52ee..2277a2d 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -48,6 +48,7 @@
#include "skeleton.dtsi"
#include "armv7-m.dtsi"
#include <dt-bindings/pinctrl/stm32f429-pinfunc.h>
+#include <dt-bindings/mfd/stm32f4-rcc.h>
/ {
clocks {
@@ -140,6 +141,18 @@
status = "disabled";
};
+ i2c1: i2c at 40005400 {
+ compatible = "st,stm32f4-i2c";
+ reg = <0x40005400 0x400>;
+ interrupts = <31>,
+ <32>;
+ resets = <&rcc STM32F4_APB1_RESET(I2C1)>;
+ clocks = <&rcc 0 STM32F4_APB1_CLOCK(I2C1)>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
usart7: serial at 40007800 {
compatible = "st,stm32-usart", "st,stm32-uart";
reg = <0x40007800 0x400>;
@@ -337,6 +350,16 @@
slew-rate = <2>;
};
};
+
+ i2c1_pins_b: i2c1 at 0 {
+ pins1 {
+ pinmux = <STM32F429_PB9_FUNC_I2C1_SDA>;
+ drive-open-drain;
+ };
+ pins2 {
+ pinmux = <STM32F429_PB6_FUNC_I2C1_SCL>;
+ };
+ };
};
rcc: rcc at 40023810 {
--
1.9.1
^ permalink raw reply related
* [PATCH v7 2/5] i2c: Add STM32F4 I2C driver
From: M'boumba Cedric Madianga @ 2016-12-22 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482413704-17531-1-git-send-email-cedric.madianga@gmail.com>
This patch adds support for the STM32F4 I2C controller.
Signed-off-by: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
---
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-stm32f4.c | 896 +++++++++++++++++++++++++++++++++++++++
3 files changed, 907 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-stm32f4.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 0cdc844..2719208 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -886,6 +886,16 @@ config I2C_ST
This driver can also be built as module. If so, the module
will be called i2c-st.
+config I2C_STM32F4
+ tristate "STMicroelectronics STM32F4 I2C support"
+ depends on ARCH_STM32 || COMPILE_TEST
+ help
+ Enable this option to add support for STM32 I2C controller embedded
+ in STM32F4 SoCs.
+
+ This driver can also be built as module. If so, the module
+ will be called i2c-stm32f4.
+
config I2C_STU300
tristate "ST Microelectronics DDC I2C interface"
depends on MACH_U300
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 1c1bac8..a2c6ff5 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
obj-$(CONFIG_I2C_ST) += i2c-st.o
+obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c
new file mode 100644
index 0000000..ca11dee
--- /dev/null
+++ b/drivers/i2c/busses/i2c-stm32f4.c
@@ -0,0 +1,896 @@
+/*
+ * Driver for STMicroelectronics STM32 I2C controller
+ *
+ * This I2C controller is described in the STM32F429/439 Soc reference manual.
+ * Please see below a link to the documentation:
+ * http://www.st.com/resource/en/reference_manual/DM00031020.pdf
+ *
+ * Copyright (C) M'boumba Cedric Madianga 2016
+ * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
+ *
+ * This driver is based on i2c-st.c
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* STM32F4 I2C offset registers */
+#define STM32F4_I2C_CR1 0x00
+#define STM32F4_I2C_CR2 0x04
+#define STM32F4_I2C_DR 0x10
+#define STM32F4_I2C_SR1 0x14
+#define STM32F4_I2C_SR2 0x18
+#define STM32F4_I2C_CCR 0x1C
+#define STM32F4_I2C_TRISE 0x20
+#define STM32F4_I2C_FLTR 0x24
+
+/* STM32F4 I2C control 1*/
+#define STM32F4_I2C_CR1_SWRST BIT(15)
+#define STM32F4_I2C_CR1_POS BIT(11)
+#define STM32F4_I2C_CR1_ACK BIT(10)
+#define STM32F4_I2C_CR1_STOP BIT(9)
+#define STM32F4_I2C_CR1_START BIT(8)
+#define STM32F4_I2C_CR1_PE BIT(0)
+
+/* STM32F4 I2C control 2 */
+#define STM32F4_I2C_CR2_FREQ_MASK GENMASK(5, 0)
+#define STM32F4_I2C_CR2_FREQ(n) (((n) & STM32F4_I2C_CR2_FREQ_MASK))
+#define STM32F4_I2C_CR2_ITBUFEN BIT(10)
+#define STM32F4_I2C_CR2_ITEVTEN BIT(9)
+#define STM32F4_I2C_CR2_ITERREN BIT(8)
+#define STM32F4_I2C_CR2_IRQ_MASK (STM32F4_I2C_CR2_ITBUFEN | \
+ STM32F4_I2C_CR2_ITEVTEN | \
+ STM32F4_I2C_CR2_ITERREN)
+
+/* STM32F4 I2C Status 1 */
+#define STM32F4_I2C_SR1_AF BIT(10)
+#define STM32F4_I2C_SR1_ARLO BIT(9)
+#define STM32F4_I2C_SR1_BERR BIT(8)
+#define STM32F4_I2C_SR1_TXE BIT(7)
+#define STM32F4_I2C_SR1_RXNE BIT(6)
+#define STM32F4_I2C_SR1_BTF BIT(2)
+#define STM32F4_I2C_SR1_ADDR BIT(1)
+#define STM32F4_I2C_SR1_SB BIT(0)
+#define STM32F4_I2C_SR1_ITEVTEN_MASK (STM32F4_I2C_SR1_BTF | \
+ STM32F4_I2C_SR1_ADDR | \
+ STM32F4_I2C_SR1_SB)
+#define STM32F4_I2C_SR1_ITBUFEN_MASK (STM32F4_I2C_SR1_TXE | \
+ STM32F4_I2C_SR1_RXNE)
+#define STM32F4_I2C_SR1_ITERREN_MASK (STM32F4_I2C_SR1_AF | \
+ STM32F4_I2C_SR1_ARLO | \
+ STM32F4_I2C_SR1_BERR)
+
+/* STM32F4 I2C Status 2 */
+#define STM32F4_I2C_SR2_BUSY BIT(1)
+
+/* STM32F4 I2C Control Clock */
+#define STM32F4_I2C_CCR_CCR_MASK GENMASK(11, 0)
+#define STM32F4_I2C_CCR_CCR(n) (((n) & STM32F4_I2C_CCR_CCR_MASK))
+#define STM32F4_I2C_CCR_FS BIT(15)
+#define STM32F4_I2C_CCR_DUTY BIT(14)
+
+/* STM32F4 I2C Trise */
+#define STM32F4_I2C_TRISE_VALUE_MASK GENMASK(5, 0)
+#define STM32F4_I2C_TRISE_VALUE(n) (((n) & STM32F4_I2C_TRISE_VALUE_MASK))
+
+/* STM32F4 I2C Filter */
+#define STM32F4_I2C_FLTR_DNF_MASK GENMASK(3, 0)
+#define STM32F4_I2C_FLTR_DNF(n) (((n) & STM32F4_I2C_FLTR_DNF_MASK))
+#define STM32F4_I2C_FLTR_ANOFF BIT(4)
+
+#define STM32F4_I2C_MIN_FREQ 2U
+#define STM32F4_I2C_MAX_FREQ 42U
+#define HZ_TO_MHZ 1000000
+
+enum stm32f4_i2c_speed {
+ STM32F4_I2C_SPEED_STANDARD, /* 100 kHz */
+ STM32F4_I2C_SPEED_FAST, /* 400 kHz */
+ STM32F4_I2C_SPEED_END,
+};
+
+/**
+ * struct stm32f4_i2c_timings - per-Mode tuning parameters
+ * @duty: Fast mode duty cycle
+ * @scl_period: SCL low/high period in microsecond
+ * @mul_ccr: Value to be multiplied to CCR to reach 100Khz/400Khz SCL frequency
+ * @min_ccr: Minimum clock ctrl reg value to reach 100Khz/400Khz SCL frequency
+ */
+struct stm32f4_i2c_timings {
+ u32 duty;
+ u32 scl_period;
+ u32 mul_ccr;
+ u32 min_ccr;
+};
+
+/**
+ * struct stm32f4_i2c_msg - client specific data
+ * @addr: 8-bit slave addr, including r/w bit
+ * @count: number of bytes to be transferred
+ * @buf: data buffer
+ * @result: result of the transfer
+ * @stop: last I2C msg to be sent, i.e. STOP to be generated
+ */
+struct stm32f4_i2c_msg {
+ u8 addr;
+ u32 count;
+ u8 *buf;
+ int result;
+ bool stop;
+};
+
+/**
+ * struct stm32f4_i2c_dev - private data of the controller
+ * @adap: I2C adapter for this controller
+ * @dev: device for this controller
+ * @base: virtual memory area
+ * @complete: completion of I2C message
+ * @clk: hw i2c clock
+ * speed: I2C clock frequency of the controller. Standard or Fast only supported
+ * @msg: I2C transfer information
+ */
+struct stm32f4_i2c_dev {
+ struct i2c_adapter adap;
+ struct device *dev;
+ void __iomem *base;
+ struct completion complete;
+ struct clk *clk;
+ int speed;
+ struct stm32f4_i2c_msg msg;
+};
+
+/*
+ * In standard mode:
+ * SCL high period = SCL low period = CCR * I2C CLK period
+ * So, CCR = SCL period * I2C CLK frequency
+ *
+ * In fast mode:
+ * DUTY = 0: Fast mode tlow/thigh = 2
+ * DUTY = 1: Fast mode tlow/thigh = 16/9
+ * If Duty = 0; SCL high period = 1 * CCR * I2C CLK period
+ * SCL low period = 2 * CCR * I2C CLK period
+ * If Duty = 1; SCL high period = 9 * CCR * I2C CLK period
+ * SCL low period = 16 * CCR * I2C CLK period
+ *
+ * Note that Duty has to bet set to reach 400khz in Fast mode
+ * So, in order to cover both SCL high/low with Duty = 1,
+ * CCR = 16 * SCL period * I2C CLK frequency
+ *
+ * Please note that the minimum allowed value is 0x04, except in FAST DUTY mode
+ * where the minimum allowed value is 0x01
+ */
+static struct stm32f4_i2c_timings i2c_timings[] = {
+ [STM32F4_I2C_SPEED_STANDARD] = {
+ .mul_ccr = 1,
+ .min_ccr = 4,
+ .duty = 0,
+ .scl_period = 5,
+ },
+ [STM32F4_I2C_SPEED_FAST] = {
+ .mul_ccr = 16,
+ .min_ccr = 1,
+ .duty = 1,
+ .scl_period = 2,
+ },
+};
+
+static inline void stm32f4_i2c_set_bits(void __iomem *reg, u32 mask)
+{
+ writel_relaxed(readl_relaxed(reg) | mask, reg);
+}
+
+static inline void stm32f4_i2c_clr_bits(void __iomem *reg, u32 mask)
+{
+ writel_relaxed(readl_relaxed(reg) & ~mask, reg);
+}
+
+static void stm32f4_i2c_soft_reset(struct stm32f4_i2c_dev *i2c_dev)
+{
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1;
+ u32 val;
+
+ val = readl_relaxed(reg);
+ writel_relaxed(val | STM32F4_I2C_CR1_SWRST, reg);
+ writel_relaxed(val, reg);
+}
+
+static void stm32f4_i2c_disable_irq(struct stm32f4_i2c_dev *i2c_dev)
+{
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
+
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_IRQ_MASK);
+}
+
+static void stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev)
+{
+ u32 clk_rate, cr2, freq;
+
+ /*
+ * The minimum allowed frequency is 2 MHz, the maximum frequency is
+ * limited by the maximum APB frequency 42 MHz
+ */
+ cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
+ cr2 &= ~STM32F4_I2C_CR2_FREQ_MASK;
+ clk_rate = clk_get_rate(i2c_dev->clk);
+ freq = DIV_ROUND_UP(clk_rate, HZ_TO_MHZ);
+ freq = clamp(freq, STM32F4_I2C_MIN_FREQ, STM32F4_I2C_MAX_FREQ);
+ cr2 |= STM32F4_I2C_CR2_FREQ(freq);
+ writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2);
+}
+
+static void stm32f4_i2c_set_rise_time(struct stm32f4_i2c_dev *i2c_dev)
+{
+ u32 trise, freq, cr2;
+
+ /*
+ * These bits must be programmed with the maximum SCL rise time given in
+ * the I2C bus specification, incremented by 1.
+ *
+ * In standard mode, the maximum allowed SCL rise time is 1000 ns.
+ * If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to
+ * 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be
+ * programmed with 09h.(1000 ns / 125 ns = 8 + 1)
+ * So, for I2C standard mode TRISE = FREQ[5:0] + 1
+ *
+ * In fast mode, the maximum allowed SCL rise time is 300 ns.
+ * If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to
+ * 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be
+ * programmed with 03h.(300 ns / 125 ns = 2 + 1)
+ * So, for I2C fast mode TRISE = FREQ[5:0] * 300 / 1000 + 1
+ */
+
+ cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
+ freq = cr2 & STM32F4_I2C_CR2_FREQ_MASK;
+
+ if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD)
+ trise = freq + 1;
+ else
+ trise = freq * 300 / 1000 + 1;
+
+ writel_relaxed(STM32F4_I2C_TRISE_VALUE(trise),
+ i2c_dev->base + STM32F4_I2C_TRISE);
+}
+
+static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_timings *t = &i2c_timings[i2c_dev->speed];
+ u32 cr2, ccr, freq, val;
+
+ ccr = readl_relaxed(i2c_dev->base + STM32F4_I2C_CCR);
+ ccr &= ~(STM32F4_I2C_CCR_FS | STM32F4_I2C_CCR_DUTY |
+ STM32F4_I2C_CCR_CCR_MASK);
+
+ /*
+ * Please see the comments above regarding i2c_timings[] declaration
+ * to understand the below calculation
+ */
+ cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
+ freq = cr2 & STM32F4_I2C_CR2_FREQ_MASK;
+ val = freq * t->scl_period * t->mul_ccr;
+ if (val < t->min_ccr)
+ val = t->min_ccr;
+ ccr |= STM32F4_I2C_CCR_CCR(val);
+
+ if (t->duty)
+ ccr |= STM32F4_I2C_CCR_FS | STM32F4_I2C_CCR_DUTY;
+
+ writel_relaxed(ccr, i2c_dev->base + STM32F4_I2C_CCR);
+}
+
+static void stm32f4_i2c_set_filter(struct stm32f4_i2c_dev *i2c_dev)
+{
+ u32 filter;
+
+ /* Enable analog noise filter and disable digital noise filter */
+ filter = readl_relaxed(i2c_dev->base + STM32F4_I2C_FLTR);
+ filter &= ~(STM32F4_I2C_FLTR_ANOFF | STM32F4_I2C_FLTR_DNF_MASK);
+ writel_relaxed(filter, i2c_dev->base + STM32F4_I2C_FLTR);
+}
+
+/**
+ * stm32f4_i2c_hw_config() - Prepare I2C block
+ * @i2c_dev: Controller's private data
+ */
+static void stm32f4_i2c_hw_config(struct stm32f4_i2c_dev *i2c_dev)
+{
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1;
+
+ /* Disable I2C */
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_PE);
+
+ stm32f4_i2c_set_periph_clk_freq(i2c_dev);
+
+ stm32f4_i2c_set_rise_time(i2c_dev);
+
+ stm32f4_i2c_set_speed_mode(i2c_dev);
+
+ stm32f4_i2c_set_filter(i2c_dev);
+
+ /* Enable I2C */
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_PE);
+}
+
+static int stm32f4_i2c_wait_free_bus(struct stm32f4_i2c_dev *i2c_dev)
+{
+ u32 status;
+ int ret;
+
+ ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F4_I2C_SR2,
+ status,
+ !(status & STM32F4_I2C_SR2_BUSY),
+ 10, 1000);
+ if (ret) {
+ dev_err(i2c_dev->dev, "bus not free\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
+/**
+ * stm32f4_i2c_write_ byte() - Write a byte in the data register
+ * @i2c_dev: Controller's private data
+ * @byte: Data to write in the register
+ */
+static void stm32f4_i2c_write_byte(struct stm32f4_i2c_dev *i2c_dev, u8 byte)
+{
+ writel_relaxed(byte, i2c_dev->base + STM32F4_I2C_DR);
+}
+
+/**
+ * stm32f4_i2c_write_msg() - Fill the data register in write mode
+ * @i2c_dev: Controller's private data
+ *
+ * This function fills the data register with I2C transfer buffer
+ */
+static void stm32f4_i2c_write_msg(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+
+ stm32f4_i2c_write_byte(i2c_dev, *msg->buf++);
+ msg->count--;
+}
+
+static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ u32 rbuf;
+
+ rbuf = readl_relaxed(i2c_dev->base + STM32F4_I2C_DR);
+ *msg->buf++ = rbuf & 0xff;
+ msg->count--;
+}
+
+static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
+
+ stm32f4_i2c_disable_irq(i2c_dev);
+
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ if (msg->stop)
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
+ else
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
+
+ complete(&i2c_dev->complete);
+}
+
+/**
+ * stm32f4_i2c_handle_write() - Handle FIFO empty interrupt in case of write
+ * @i2c_dev: Controller's private data
+ */
+static void stm32f4_i2c_handle_write(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
+
+ if (msg->count) {
+ stm32f4_i2c_write_msg(i2c_dev);
+ if (!msg->count) {
+ /* Disable buffer interrupts for RXNE/TXE events */
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN);
+ }
+ } else {
+ stm32f4_i2c_terminate_xfer(i2c_dev);
+ }
+}
+
+/**
+ * stm32f4_i2c_handle_read() - Handle FIFO empty interrupt in case of read
+ * @i2c_dev: Controller's private data
+ */
+static void stm32f4_i2c_handle_read(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
+
+ switch (msg->count) {
+ case 1:
+ stm32f4_i2c_disable_irq(i2c_dev);
+ stm32f4_i2c_read_msg(i2c_dev);
+ complete(&i2c_dev->complete);
+ break;
+ case 2:
+ case 3:
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN);
+ break;
+ default:
+ stm32f4_i2c_read_msg(i2c_dev);
+ }
+}
+
+/**
+ * stm32f4_i2c_handle_rx_btf() - Handle byte transfer finished interrupt
+ * in case of read
+ * @i2c_dev: Controller's private data
+ */
+static void stm32f4_i2c_handle_rx_btf(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg;
+ u32 mask;
+ int i;
+
+ switch (msg->count) {
+ case 2:
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ /* Generate STOP or repeated Start */
+ if (msg->stop)
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
+ else
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
+
+ /* Read two last data bytes */
+ for (i = 2; i > 0; i--)
+ stm32f4_i2c_read_msg(i2c_dev);
+
+ /* Disable events and error interrupts */
+ reg = i2c_dev->base + STM32F4_I2C_CR2;
+ mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN;
+ stm32f4_i2c_clr_bits(reg, mask);
+
+ complete(&i2c_dev->complete);
+ break;
+ case 3:
+ /* Enable ACK and read data */
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK);
+ stm32f4_i2c_read_msg(i2c_dev);
+ break;
+ default:
+ stm32f4_i2c_read_msg(i2c_dev);
+ }
+}
+
+/**
+ * stm32f4_i2c_handle_rx_addr() - Handle address matched interrupt in case of
+ * master receiver
+ * @i2c_dev: Controller's private data
+ */
+static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev)
+{
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg;
+
+ switch (msg->count) {
+ case 0:
+ stm32f4_i2c_terminate_xfer(i2c_dev);
+ /* Clear ADDR flag */
+ readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
+ break;
+ case 1:
+ /*
+ * Single byte reception:
+ * Enable NACK, clear ADDR flag and generate STOP or RepSTART
+ */
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK);
+ readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
+ if (msg->stop)
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
+ else
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
+ break;
+ case 2:
+ /*
+ * 2-byte reception:
+ * Enable NACK and set POS
+ */
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK);
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_POS);
+ readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
+ break;
+
+ default:
+ /* N-byte reception: Enable ACK */
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_ACK);
+ readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
+ break;
+ }
+}
+
+/**
+ * stm32f4_i2c_isr_event() - Interrupt routine for I2C bus event
+ * @irq: interrupt number
+ * @data: Controller's private data
+ */
+static irqreturn_t stm32f4_i2c_isr_event(int irq, void *data)
+{
+ struct stm32f4_i2c_dev *i2c_dev = data;
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg;
+ u32 status, possible_status, ien;
+ int flag;
+
+ ien = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
+ ien &= STM32F4_I2C_CR2_IRQ_MASK;
+ possible_status = 0;
+
+ /* Check possible status combinations */
+ if (ien & STM32F4_I2C_CR2_ITEVTEN) {
+ possible_status = STM32F4_I2C_SR1_ITEVTEN_MASK;
+ if (ien & STM32F4_I2C_CR2_ITBUFEN)
+ possible_status |= STM32F4_I2C_SR1_ITBUFEN_MASK;
+ }
+
+ status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1);
+
+ if (!(status & possible_status)) {
+ dev_dbg(i2c_dev->dev,
+ "spurious evt irq (status=0x%08x, ien=0x%08x)\n",
+ status, ien);
+ return IRQ_NONE;
+ }
+
+ while (status & possible_status) {
+ /* Use __fls() to check error bits first */
+ flag = __fls(status & possible_status);
+
+ switch (1 << flag) {
+ case STM32F4_I2C_SR1_SB:
+ stm32f4_i2c_write_byte(i2c_dev, msg->addr);
+ break;
+
+ case STM32F4_I2C_SR1_ADDR:
+ if (msg->addr & I2C_M_RD)
+ stm32f4_i2c_handle_rx_addr(i2c_dev);
+ else
+ readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
+
+ /* Enable buffer interrupts for RXNE/TXE events */
+ reg = i2c_dev->base + STM32F4_I2C_CR2;
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR2_ITBUFEN);
+ possible_status |= STM32F4_I2C_SR1_ITBUFEN_MASK;
+ break;
+
+ case STM32F4_I2C_SR1_BTF:
+ if (msg->addr & I2C_M_RD)
+ stm32f4_i2c_handle_rx_btf(i2c_dev);
+ else
+ stm32f4_i2c_handle_write(i2c_dev);
+ break;
+
+ case STM32F4_I2C_SR1_TXE:
+ stm32f4_i2c_handle_write(i2c_dev);
+ break;
+
+ case STM32F4_I2C_SR1_RXNE:
+ stm32f4_i2c_handle_read(i2c_dev);
+ break;
+
+ default:
+ dev_err(i2c_dev->dev,
+ "evt irq unhandled: status=0x%08x)\n",
+ status);
+ return IRQ_NONE;
+ }
+ status &= ~(1 << flag);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * stm32f4_i2c_isr_error() - Interrupt routine for I2C bus error
+ * @irq: interrupt number
+ * @data: Controller's private data
+ */
+static irqreturn_t stm32f4_i2c_isr_error(int irq, void *data)
+{
+ struct stm32f4_i2c_dev *i2c_dev = data;
+ struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
+ void __iomem *reg;
+ u32 status, possible_status, ien;
+ int flag;
+
+ ien = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
+ ien &= STM32F4_I2C_CR2_IRQ_MASK;
+ possible_status = 0;
+
+ /* Check possible status combinations */
+ if (ien & STM32F4_I2C_CR2_ITERREN)
+ possible_status = STM32F4_I2C_SR1_ITERREN_MASK;
+
+ status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1);
+
+ if (!(status & possible_status)) {
+ dev_dbg(i2c_dev->dev,
+ "spurious err it (status=0x%08x, ien=0x%08x)\n",
+ status, ien);
+ return IRQ_NONE;
+ }
+
+ /* Use __fls() to check error bits first */
+ flag = __fls(status & possible_status);
+
+ switch (1 << flag) {
+ case STM32F4_I2C_SR1_BERR:
+ reg = i2c_dev->base + STM32F4_I2C_SR1;
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_SR1_BERR);
+ msg->result = -EIO;
+ break;
+
+ case STM32F4_I2C_SR1_ARLO:
+ reg = i2c_dev->base + STM32F4_I2C_SR1;
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_SR1_ARLO);
+ msg->result = -EAGAIN;
+ break;
+
+ case STM32F4_I2C_SR1_AF:
+ reg = i2c_dev->base + STM32F4_I2C_CR1;
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
+ msg->result = -EIO;
+ break;
+
+ default:
+ dev_err(i2c_dev->dev,
+ "err it unhandled: status=0x%08x)\n", status);
+ return IRQ_NONE;
+ }
+
+ stm32f4_i2c_soft_reset(i2c_dev);
+ stm32f4_i2c_disable_irq(i2c_dev);
+ complete(&i2c_dev->complete);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * stm32f4_i2c_xfer_msg() - Transfer a single I2C message
+ * @i2c_dev: Controller's private data
+ * @msg: I2C message to transfer
+ * @is_first: first message of the sequence
+ * @is_last: last message of the sequence
+ */
+static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev,
+ struct i2c_msg *msg, bool is_first,
+ bool is_last)
+{
+ struct stm32f4_i2c_msg *f4_msg = &i2c_dev->msg;
+ void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1;
+ unsigned long timeout;
+ u32 mask;
+ int ret;
+
+ f4_msg->addr = i2c_8bit_addr_from_msg(msg);
+ f4_msg->buf = msg->buf;
+ f4_msg->count = msg->len;
+ f4_msg->result = 0;
+ f4_msg->stop = is_last;
+
+ reinit_completion(&i2c_dev->complete);
+
+ /* Enable events and errors interrupts */
+ mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN;
+ stm32f4_i2c_set_bits(i2c_dev->base + STM32F4_I2C_CR2, mask);
+
+ if (is_first) {
+ ret = stm32f4_i2c_wait_free_bus(i2c_dev);
+ if (ret)
+ return ret;
+
+ /* START generation */
+ stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
+ }
+
+ timeout = wait_for_completion_timeout(&i2c_dev->complete,
+ i2c_dev->adap.timeout);
+ ret = f4_msg->result;
+
+ /* Disable PEC position Ack */
+ stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_POS);
+
+ if (!timeout)
+ ret = -ETIMEDOUT;
+
+ return ret;
+}
+
+/**
+ * stm32f4_i2c_xfer() - Transfer combined I2C message
+ * @i2c_adap: Adapter pointer to the controller
+ * @msgs: Pointer to data to be written.
+ * @num: Number of messages to be executed
+ */
+static int stm32f4_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct stm32f4_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
+ int ret, i;
+
+ ret = clk_enable(i2c_dev->clk);
+ if (ret) {
+ dev_err(i2c_dev->dev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ stm32f4_i2c_hw_config(i2c_dev);
+
+ for (i = 0; i < num && !ret; i++)
+ ret = stm32f4_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0,
+ i == num - 1);
+
+ clk_disable(i2c_dev->clk);
+
+ return (ret < 0) ? ret : num;
+}
+
+static u32 stm32f4_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm stm32f4_i2c_algo = {
+ .master_xfer = stm32f4_i2c_xfer,
+ .functionality = stm32f4_i2c_func,
+};
+
+static int stm32f4_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct stm32f4_i2c_dev *i2c_dev;
+ struct resource *res;
+ u32 irq_event, irq_error, clk_rate;
+ struct i2c_adapter *adap;
+ struct reset_control *rst;
+ int ret;
+
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c_dev->base))
+ return PTR_ERR(i2c_dev->base);
+
+ irq_event = irq_of_parse_and_map(np, 0);
+ if (!irq_event) {
+ dev_err(&pdev->dev, "IRQ event missing or invalid\n");
+ return -EINVAL;
+ }
+
+ irq_error = irq_of_parse_and_map(np, 1);
+ if (!irq_error) {
+ dev_err(&pdev->dev, "IRQ error missing or invalid\n");
+ return -EINVAL;
+ }
+
+ i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_dev->clk)) {
+ dev_err(&pdev->dev, "Error: Missing controller clock\n");
+ return PTR_ERR(i2c_dev->clk);
+ }
+ ret = clk_prepare(i2c_dev->clk);
+ if (ret) {
+ dev_err(i2c_dev->dev, "Failed to prepare clock\n");
+ return ret;
+ }
+
+ rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(rst)) {
+ dev_err(&pdev->dev, "Error: Missing controller reset\n");
+ ret = PTR_ERR(rst);
+ goto clk_free;
+ }
+ reset_control_assert(rst);
+ udelay(2);
+ reset_control_deassert(rst);
+
+ i2c_dev->speed = STM32F4_I2C_SPEED_STANDARD;
+ ret = of_property_read_u32(np, "clock-frequency", &clk_rate);
+ if (!ret && clk_rate >= 40000)
+ i2c_dev->speed = STM32F4_I2C_SPEED_FAST;
+
+ i2c_dev->dev = &pdev->dev;
+
+ ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0,
+ pdev->name, i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq event %i\n",
+ irq_event);
+ goto clk_free;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0,
+ pdev->name, i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq error %i\n",
+ irq_error);
+ goto clk_free;
+ }
+
+ adap = &i2c_dev->adap;
+ i2c_set_adapdata(adap, i2c_dev);
+ snprintf(adap->name, sizeof(adap->name), "STM32 I2C(%pa)", &res->start);
+ adap->owner = THIS_MODULE;
+ adap->timeout = 2 * HZ;
+ adap->retries = 0;
+ adap->algo = &stm32f4_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
+
+ init_completion(&i2c_dev->complete);
+
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ goto clk_free;
+
+ platform_set_drvdata(pdev, i2c_dev);
+
+ dev_info(i2c_dev->dev, "STM32F4 I2C driver registered\n");
+
+ return 0;
+
+clk_free:
+ clk_unprepare(i2c_dev->clk);
+ return ret;
+}
+
+static int stm32f4_i2c_remove(struct platform_device *pdev)
+{
+ struct stm32f4_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c_dev->adap);
+
+ clk_unprepare(i2c_dev->clk);
+
+ return 0;
+}
+
+static const struct of_device_id stm32f4_i2c_match[] = {
+ { .compatible = "st,stm32f4-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32f4_i2c_match);
+
+static struct platform_driver stm32f4_i2c_driver = {
+ .driver = {
+ .name = "stm32f4-i2c",
+ .of_match_table = stm32f4_i2c_match,
+ },
+ .probe = stm32f4_i2c_probe,
+ .remove = stm32f4_i2c_remove,
+};
+
+module_platform_driver(stm32f4_i2c_driver);
+
+MODULE_AUTHOR("M'boumba Cedric Madianga <cedric.madianga@gmail.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 I2C driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH v7 1/5] dt-bindings: Document the STM32 I2C bindings
From: M'boumba Cedric Madianga @ 2016-12-22 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482413704-17531-1-git-send-email-cedric.madianga@gmail.com>
This patch adds documentation of device tree bindings for the STM32 I2C
controller.
Signed-off-by: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/i2c/i2c-stm32.txt | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-stm32.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-stm32.txt b/Documentation/devicetree/bindings/i2c/i2c-stm32.txt
new file mode 100644
index 0000000..78eaf7b
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-stm32.txt
@@ -0,0 +1,33 @@
+* I2C controller embedded in STMicroelectronics STM32 I2C platform
+
+Required properties :
+- compatible : Must be "st,stm32f4-i2c"
+- reg : Offset and length of the register set for the device
+- interrupts : Must contain the interrupt id for I2C event and then the
+ interrupt id for I2C error.
+- resets: Must contain the phandle to the reset controller.
+- clocks: Must contain the input clock of the I2C instance.
+- A pinctrl state named "default" must be defined to set pins in mode of
+ operation for I2C transfer
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties :
+- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
+ the default 100 kHz frequency will be used. As only Normal and Fast modes
+ are supported, possible values are 100000 and 400000.
+
+Example :
+
+ i2c at 40005400 {
+ compatible = "st,stm32f4-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x40005400 0x400>;
+ interrupts = <31>,
+ <32>;
+ resets = <&rcc 277>;
+ clocks = <&rcc 0 149>;
+ pinctrl-0 = <&i2c1_sda_pin>, <&i2c1_scl_pin>;
+ pinctrl-names = "default";
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v7 0/5] Add support for the STM32F4 I2C
From: M'boumba Cedric Madianga @ 2016-12-22 13:34 UTC (permalink / raw)
To: linux-arm-kernel
This patchset adds support for the I2C controller embedded in STM32F4xx SoC.
It enables I2C transfer in interrupt mode with Standard-mode and Fast-mode bus
speed.
Changes since v6:
- Add commit message for the patches in defconfig, .dtsi and .dts files (Alex)
- Order I2C instance base address in .dtsi file (Alex)
- Add commit message for the patch in stm32429i-eval.dts (Alex)
- Add link to the STM32F4 Soc ref manual where I2C device is described (Uwe)
- Use more usal way to define constants with several lines (Uwe)
- Remove rate variable from stm32f4_i2c_timings as it is not used (Uwe)
- Remove irq variable from stm32f4_i2c_dev struct are they are only needed
during probe (Uwe)
- Add comment from datasheet to explain stm32f4_i2c_timings values (Uwe)
- Rework i2c soft_reset implementation (Uwe)
- Replace "it" by "irq" as it is a more usual abbreviation for interrupt (Uwe)
- Add comment from datasheet to explain periph clk freq calculation (Uwe)
- Use DIV_ROUND_UP instead of plain division when required (Uwe)
- Add comment from datasheet to explain timing rise calculation (Uwe)
- Rework timing rise calculation by using shorter computation (Uwe)
- Remove (u8) cast when reading I2C data register (Uwe)
- Rework isr_event routine to handle several events during one call of the
routine (Uwe)
- Precise which type of irq is failed when a irq request error occurs (Uwe)
- Use devm_request_irq() instead of devm_request_threaded_irq() to avoid
spurious evt irq when clearing status registers in threaded context
M'boumba Cedric Madianga (5):
dt-bindings: Document the STM32 I2C bindings
i2c: Add STM32F4 I2C driver
ARM: dts: stm32: Add I2C1 support for STM32F429 SoC
ARM: dts: stm32: Add I2C1 support for STM32429 eval board
ARM: configs: stm32: Add I2C support for STM32 defconfig
.../devicetree/bindings/i2c/i2c-stm32.txt | 33 +
arch/arm/boot/dts/stm32429i-eval.dts | 6 +
arch/arm/boot/dts/stm32f429.dtsi | 23 +
arch/arm/configs/stm32_defconfig | 3 +
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-stm32f4.c | 896 +++++++++++++++++++++
7 files changed, 972 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-stm32.txt
create mode 100644 drivers/i2c/busses/i2c-stm32f4.c
--
1.9.1
^ permalink raw reply
* [PATCH] drm: zte: support hdmi audio through spdif
From: Shawn Guo @ 2016-12-22 13:11 UTC (permalink / raw)
To: linux-arm-kernel
From: Shawn Guo <shawn.guo@linaro.org>
It enables HDMI audio support through SPDIF interface based on generic
hdmi-audio-codec driver. The HDMI hardware supports more audio
interfaces than SPDIF, like I2S, which may be added later.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
drivers/gpu/drm/zte/Kconfig | 1 +
drivers/gpu/drm/zte/zx_hdmi.c | 169 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/zte/zx_hdmi_regs.h | 14 +++
drivers/gpu/drm/zte/zx_vou.c | 9 ++
drivers/gpu/drm/zte/zx_vou.h | 10 +++
drivers/gpu/drm/zte/zx_vou_regs.h | 2 +
6 files changed, 205 insertions(+)
diff --git a/drivers/gpu/drm/zte/Kconfig b/drivers/gpu/drm/zte/Kconfig
index 4065b2840f1c..ed6de4b10c74 100644
--- a/drivers/gpu/drm/zte/Kconfig
+++ b/drivers/gpu/drm/zte/Kconfig
@@ -4,5 +4,6 @@ config DRM_ZTE
select DRM_KMS_CMA_HELPER
select DRM_KMS_FB_HELPER
select DRM_KMS_HELPER
+ select SND_SOC_HDMI_CODEC if SND_SOC
help
Choose this option to enable DRM on ZTE ZX SoCs.
diff --git a/drivers/gpu/drm/zte/zx_hdmi.c b/drivers/gpu/drm/zte/zx_hdmi.c
index 6bf6c364811e..e0b1bbfcc685 100644
--- a/drivers/gpu/drm/zte/zx_hdmi.c
+++ b/drivers/gpu/drm/zte/zx_hdmi.c
@@ -25,6 +25,8 @@
#include <drm/drm_of.h>
#include <drm/drmP.h>
+#include <sound/hdmi-codec.h>
+
#include "zx_hdmi_regs.h"
#include "zx_vou.h"
@@ -49,6 +51,7 @@ struct zx_hdmi {
bool sink_is_hdmi;
bool sink_has_audio;
const struct vou_inf *inf;
+ struct platform_device *audio_pdev;
};
#define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x)
@@ -366,6 +369,163 @@ static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
+static int zx_hdmi_audio_startup(struct device *dev, void *data)
+{
+ struct zx_hdmi *hdmi = dev_get_drvdata(dev);
+ struct drm_encoder *encoder = &hdmi->encoder;
+
+ vou_inf_hdmi_audio_sel(encoder->crtc, VOU_HDMI_AUD_SPDIF);
+
+ return 0;
+}
+
+static void zx_hdmi_audio_shutdown(struct device *dev, void *data)
+{
+ struct zx_hdmi *hdmi = dev_get_drvdata(dev);
+
+ /* Disable audio input */
+ hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, 0);
+}
+
+static int zx_hdmi_audio_get_n(unsigned int fs)
+{
+ unsigned int n;
+
+ switch (fs) {
+ case 32000:
+ n = 4096;
+ break;
+ case 44100:
+ n = 6272;
+ break;
+ case 48000:
+ n = 6144;
+ break;
+ case 88200:
+ n = 6272 * 2;
+ break;
+ case 96000:
+ n = 6144 * 2;
+ break;
+ case 176400:
+ n = 6272 * 4;
+ break;
+ case 192000:
+ n = 6144 * 4;
+ break;
+ default:
+ n = fs * 128 / 1000;
+ }
+
+ return n;
+}
+
+static int zx_hdmi_audio_hw_params(struct device *dev,
+ void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct zx_hdmi *hdmi = dev_get_drvdata(dev);
+ struct hdmi_audio_infoframe *cea = ¶ms->cea;
+ union hdmi_infoframe frame;
+ int n;
+
+ /* We only support spdif for now */
+ if (daifmt->fmt != HDMI_SPDIF) {
+ DRM_DEV_ERROR(hdmi->dev, "invalid daifmt %d\n", daifmt->fmt);
+ return -EINVAL;
+ }
+
+ switch (params->sample_width) {
+ case 16:
+ hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
+ SPDIF_SAMPLE_SIZE_16BIT);
+ break;
+ case 20:
+ hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
+ SPDIF_SAMPLE_SIZE_20BIT);
+ break;
+ case 24:
+ hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
+ SPDIF_SAMPLE_SIZE_24BIT);
+ break;
+ default:
+ DRM_DEV_ERROR(hdmi->dev, "invalid sample width %d\n",
+ params->sample_width);
+ return -EINVAL;
+ }
+
+ /* CTS is calculated by hardware, and we only need to take care of N */
+ n = zx_hdmi_audio_get_n(params->sample_rate);
+ hdmi_writeb(hdmi, N_SVAL1, n & 0xff);
+ hdmi_writeb(hdmi, N_SVAL2, (n >> 8) && 0xff);
+ hdmi_writeb(hdmi, N_SVAL3, (n >> 16) & 0xf);
+
+ /* Enable spdif mode */
+ hdmi_writeb_mask(hdmi, AUD_MODE, SPDIF_EN, SPDIF_EN);
+
+ /* Enable audio input */
+ hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, AUD_IN_EN);
+
+ memcpy(&frame.audio, cea, sizeof(*cea));
+
+ return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AUDIO);
+}
+
+static int zx_hdmi_audio_digital_mute(struct device *dev, void *data,
+ bool enable)
+{
+ struct zx_hdmi *hdmi = dev_get_drvdata(dev);
+
+ if (enable)
+ hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE,
+ TPI_AUD_MUTE);
+ else
+ hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 0);
+
+ return 0;
+}
+
+static int zx_hdmi_audio_get_eld(struct device *dev, void *data,
+ uint8_t *buf, size_t len)
+{
+ struct zx_hdmi *hdmi = dev_get_drvdata(dev);
+ struct drm_connector *connector = &hdmi->connector;
+
+ memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
+
+ return 0;
+}
+
+static const struct hdmi_codec_ops zx_hdmi_codec_ops = {
+ .audio_startup = zx_hdmi_audio_startup,
+ .hw_params = zx_hdmi_audio_hw_params,
+ .audio_shutdown = zx_hdmi_audio_shutdown,
+ .digital_mute = zx_hdmi_audio_digital_mute,
+ .get_eld = zx_hdmi_audio_get_eld,
+};
+
+static struct hdmi_codec_pdata zx_hdmi_codec_pdata = {
+ .ops = &zx_hdmi_codec_ops,
+ .spdif = 1,
+};
+
+static int zx_hdmi_audio_register(struct zx_hdmi *hdmi)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_data(hdmi->dev, HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO,
+ &zx_hdmi_codec_pdata,
+ sizeof(zx_hdmi_codec_pdata));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ hdmi->audio_pdev = pdev;
+
+ return 0;
+}
+
static int zx_hdmi_i2c_read(struct zx_hdmi *hdmi, struct i2c_msg *msg)
{
int len = msg->len;
@@ -566,6 +726,12 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
return ret;
}
+ ret = zx_hdmi_audio_register(hdmi);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to register audio: %d\n", ret);
+ return ret;
+ }
+
ret = zx_hdmi_register(drm, hdmi);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register hdmi: %d\n", ret);
@@ -590,6 +756,9 @@ static void zx_hdmi_unbind(struct device *dev, struct device *master,
hdmi->connector.funcs->destroy(&hdmi->connector);
hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+ if (hdmi->audio_pdev)
+ platform_device_unregister(hdmi->audio_pdev);
}
static const struct component_ops zx_hdmi_component_ops = {
diff --git a/drivers/gpu/drm/zte/zx_hdmi_regs.h b/drivers/gpu/drm/zte/zx_hdmi_regs.h
index de911f66b658..c6d5d8211725 100644
--- a/drivers/gpu/drm/zte/zx_hdmi_regs.h
+++ b/drivers/gpu/drm/zte/zx_hdmi_regs.h
@@ -52,5 +52,19 @@
#define TPI_INFO_TRANS_RPT BIT(6)
#define TPI_DDC_MASTER_EN 0x06f8
#define HW_DDC_MASTER BIT(7)
+#define N_SVAL1 0xa03
+#define N_SVAL2 0xa04
+#define N_SVAL3 0xa05
+#define AUD_EN 0xa13
+#define AUD_IN_EN BIT(0)
+#define AUD_MODE 0xa14
+#define SPDIF_EN BIT(1)
+#define TPI_AUD_CONFIG 0xa62
+#define SPDIF_SAMPLE_SIZE_SHIFT 6
+#define SPDIF_SAMPLE_SIZE_MASK (0x3 << SPDIF_SAMPLE_SIZE_SHIFT)
+#define SPDIF_SAMPLE_SIZE_16BIT (0x1 << SPDIF_SAMPLE_SIZE_SHIFT)
+#define SPDIF_SAMPLE_SIZE_20BIT (0x2 << SPDIF_SAMPLE_SIZE_SHIFT)
+#define SPDIF_SAMPLE_SIZE_24BIT (0x3 << SPDIF_SAMPLE_SIZE_SHIFT)
+#define TPI_AUD_MUTE BIT(4)
#endif /* __ZX_HDMI_REGS_H__ */
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index 8ca9c4bdeeaf..b39fbb71960a 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -148,6 +148,15 @@ static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
return zcrtc->vou;
}
+void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
+ enum vou_inf_hdmi_audio aud)
+{
+ struct zx_crtc *zcrtc = to_zx_crtc(crtc);
+ struct zx_vou_hw *vou = zcrtc->vou;
+
+ zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud);
+}
+
void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
{
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
index 1559c1f79db7..97b8f97ee3a7 100644
--- a/drivers/gpu/drm/zte/zx_vou.h
+++ b/drivers/gpu/drm/zte/zx_vou.h
@@ -30,6 +30,14 @@ enum vou_inf_data_sel {
VOU_RGB_666 = 3,
};
+enum vou_inf_hdmi_audio {
+ VOU_HDMI_AUD_SPDIF = BIT(0),
+ VOU_HDMI_AUD_I2S = BIT(1),
+ VOU_HDMI_AUD_DSD = BIT(2),
+ VOU_HDMI_AUD_HBR = BIT(3),
+ VOU_HDMI_AUD_PARALLEL = BIT(4),
+};
+
struct vou_inf {
enum vou_inf_id id;
enum vou_inf_data_sel data_sel;
@@ -37,6 +45,8 @@ struct vou_inf {
u32 clocks_sel_bits;
};
+void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
+ enum vou_inf_hdmi_audio aud);
void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc);
void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
index 193c1ce01fe7..48fb90bd4594 100644
--- a/drivers/gpu/drm/zte/zx_vou_regs.h
+++ b/drivers/gpu/drm/zte/zx_vou_regs.h
@@ -168,6 +168,8 @@
#define VOU_CLK_GL0_SEL BIT(4)
#define VOU_CLK_REQEN 0x20
#define VOU_CLK_EN 0x24
+#define VOU_INF_HDMI_CTRL 0x30
+#define VOU_HDMI_AUD_MASK 0x1f
/* OTFPPU_CTRL registers */
#define OTFPPU_RSZ_DATA_SOURCE 0x04
--
1.9.1
^ permalink raw reply related
* [PATCH 1/2] soc: ti: Use remoteproc auto_boot feature
From: Bjorn Andersson @ 2016-12-22 13:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1f5b2631-f744-a5c1-55c1-82eb27d5cbd7@ti.com>
On Wed 21 Dec 19:16 PST 2016, Suman Anna wrote:
> Hi Sarang,
>
> On 12/15/2016 06:03 PM, Sarangdhar Joshi wrote:
> > The function wkup_m3_rproc_boot_thread waits for asynchronous
> > firmware loading to complete successfully before calling
> > rproc_boot(). The same can be achieved by just setting
> > rproc->auto_boot flag. Change this. As a result this change
> > removes wkup_m3_rproc_boot_thread and moves m3_ipc->sync_complete
> > initialization to the wkup_m3_ipc_probe().
> >
> > Other than the current usage, the firmware_loading_complete is
> > only used in rproc_del() where it's no longer needed. This
> > change is in preparation for removing firmware_loading_complete
> > completely.
>
> Based on the comments so far, I am assuming that you are dropping this
> series.
>
Following up on those comments only revealed that we have several other
similar race conditions, so I'm hoping that Sarangdhar will continue to
work on fixing those - and in this process get rid of this completion.
> In any case, this series did break our PM stack. We definitely don't
> want to auto-boot the wkup_m3_rproc device, that responsibility will
> need to stay with the wkup_m3_ipc driver.
>
Reviewing the wkup_m3 situation again I see that as we have moved the
resource table parsing to the rproc_boot() path there's no longer any
need for the wkup_m3_ipc driver to wait for the remoteproc-core-internal
completion.
If rproc_get_by_phandle() returns non-NULL it is initialized. We still
don't want to call rproc_boot() from wkup_m3_ipc_probe(), so let's keep
the wkup_m3_rproc_boot_thread().
Sarangdhar, could you update the wkup_m3_ipc patch to just drop the
wait_for_completion() call?
Regards,
Bjorn
^ permalink raw reply
* [RFC v4 15/16] vfio/type1: Check MSI remapping at irq domain level
From: Auger Eric @ 2016-12-22 13:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <HE1PR04MB13219CC90CE2BBE62356D94CFF920@HE1PR04MB1321.eurprd04.prod.outlook.com>
Hi Diana,
On 22/12/2016 13:41, Diana Madalina Craciun wrote:
> Hi Eric,
>
> On 12/13/2016 10:32 PM, Eric Auger wrote:
>> In case the IOMMU does not bypass MSI transactions (typical
>> case on ARM), we check all MSI controllers are IRQ remapping
>> capable. If not the IRQ assignment may be unsafe.
>>
>> At this stage the arm-smmu-(v3) still advertise the
>> IOMMU_CAP_INTR_REMAP capability at IOMMU level. This will be
>> removed in subsequent patches.
>>
>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>> ---
>> drivers/vfio/vfio_iommu_type1.c | 9 ++++++---
>> 1 file changed, 6 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
>> index d07fe73..a05648b 100644
>> --- a/drivers/vfio/vfio_iommu_type1.c
>> +++ b/drivers/vfio/vfio_iommu_type1.c
>> @@ -37,6 +37,7 @@
>> #include <linux/vfio.h>
>> #include <linux/workqueue.h>
>> #include <linux/dma-iommu.h>
>> +#include <linux/irqdomain.h>
>>
>> #define DRIVER_VERSION "0.2"
>> #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
>> @@ -765,7 +766,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
>> struct vfio_domain *domain, *d;
>> struct bus_type *bus = NULL;
>> int ret;
>> - bool resv_msi;
>> + bool resv_msi, msi_remap;
>> phys_addr_t resv_msi_base;
>>
>> mutex_lock(&iommu->lock);
>> @@ -818,8 +819,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
>> INIT_LIST_HEAD(&domain->group_list);
>> list_add(&group->next, &domain->group_list);
>>
>> - if (!allow_unsafe_interrupts &&
>> - !iommu_capable(bus, IOMMU_CAP_INTR_REMAP)) {
>> + msi_remap = resv_msi ? irq_domain_check_msi_remap() :
>> + iommu_capable(bus, IOMMU_CAP_INTR_REMAP);
>> +
>> + if (!allow_unsafe_interrupts && !msi_remap) {
>> pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
>> __func__);
>> ret = -EPERM;
>
> I tested your v4.9-reserved-v4 branch on a ITS capable hardware (NXP
> LS2080), so I did not set allow_unsafe_interrupts. It fails here
> complaining that the there is no interrupt remapping support. The
> irq_domain_check_msi_remap function returns false as none of the checked
> domains has the IRQ_DOMAIN_FLAG_MSI_REMAP flag set. I think the reason
> is that the flags are not propagated through the domain hierarchy when
> the domain is created.
Hum OK. Please apologize for the inconvenience, all the more so this is
the second time you report the same issue for different cause :-( At the
moment I can't test on a GICv3 ITS based system. I will try to fix that
though.
I would like to get the confirmation introducing this flag is the right
direction though.
Thanks
Eric
>
> Thanks,
>
> Diana
>
>
>
^ permalink raw reply
* [PATCH v5 09/14] ACPI: platform: setup MSI domain for ACPI based platform device
From: Rafael J. Wysocki @ 2016-12-22 12:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482384922-21507-10-git-send-email-guohanjun@huawei.com>
On Thu, Dec 22, 2016 at 6:35 AM, Hanjun Guo <guohanjun@huawei.com> wrote:
> From: Hanjun Guo <hanjun.guo@linaro.org>
>
> With the platform msi domain created, we can set up the msi domain
> for a platform device when it's probed.
>
> In order to do that, we need to get the domain that the platform
> device connecting to, so the iort_get_platform_device_domain() is
> introduced to retrieve the domain from iort.
>
> After the domain is retrieved, we need a proper way to set the
> domain to paltform device, as some platform devices such as an
> irqchip needs the msi irqdomain to be the interrupt parent domain,
> we need to get irqdomain before platform device is probed but after
> the platform device is allocated, so introduce a callback (pre_add_cb)
> in pdevinfo to prepare firmware related information which is needed
> for device probe, then set the msi domain in that callback.
>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
> Cc: Greg KH <gregkh@linuxfoundation.org>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> ---
> drivers/acpi/acpi_platform.c | 11 +++++++++++
> drivers/acpi/arm64/iort.c | 43 +++++++++++++++++++++++++++++++++++++++++
> drivers/base/platform.c | 3 +++
> include/linux/acpi_iort.h | 3 +++
> include/linux/platform_device.h | 3 +++
> 5 files changed, 63 insertions(+)
>
> diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
> index b4c1a6a..5d8d61b4 100644
> --- a/drivers/acpi/acpi_platform.c
> +++ b/drivers/acpi/acpi_platform.c
> @@ -12,6 +12,7 @@
> */
>
> #include <linux/acpi.h>
> +#include <linux/acpi_iort.h>
> #include <linux/device.h>
> #include <linux/err.h>
> #include <linux/kernel.h>
> @@ -48,6 +49,15 @@ static void acpi_platform_fill_resource(struct acpi_device *adev,
> }
>
> /**
> + * acpi_platform_pre_add_cb - callback before platform device is added, to
> + * prepare firmware related information which is needed for device probe
> + */
> +static void acpi_platform_pre_add_cb(struct device *dev)
> +{
> + acpi_configure_pmsi_domain(dev);
> +}
> +
> +/**
> * acpi_create_platform_device - Create platform device for ACPI device node
> * @adev: ACPI device node to create a platform device for.
> * @properties: Optional collection of build-in properties.
> @@ -109,6 +119,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
> pdevinfo.num_res = count;
> pdevinfo.fwnode = acpi_fwnode_handle(adev);
> pdevinfo.properties = properties;
> + pdevinfo.pre_add_cb = acpi_platform_pre_add_cb;
Why don't you point that directly to acpi_configure_pmsi_domain()? It
doesn't look like the wrapper is necessary at all.
And I'm not sure why the new callback is necessary ->
>
> if (acpi_dma_supported(adev))
> pdevinfo.dma_mask = DMA_BIT_MASK(32);
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index bc68d93..6b72fcb 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -527,6 +527,49 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id)
> return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
> }
>
> +/**
> + * iort_get_platform_device_domain() - Find MSI domain related to a
> + * platform device
> + * @dev: the dev pointer associated with the platform device
> + *
> + * Returns: the MSI domain for this device, NULL otherwise
> + */
> +static struct irq_domain *iort_get_platform_device_domain(struct device *dev)
> +{
> + struct acpi_iort_node *node, *msi_parent;
> + struct fwnode_handle *iort_fwnode;
> + struct acpi_iort_its_group *its;
> +
> + /* find its associated iort node */
> + node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
> + iort_match_node_callback, dev);
> + if (!node)
> + return NULL;
> +
> + /* then find its msi parent node */
> + msi_parent = iort_node_get_id(node, NULL, IORT_MSI_TYPE, 0);
> + if (!msi_parent)
> + return NULL;
> +
> + /* Move to ITS specific data */
> + its = (struct acpi_iort_its_group *)msi_parent->node_data;
> +
> + iort_fwnode = iort_find_domain_token(its->identifiers[0]);
> + if (!iort_fwnode)
> + return NULL;
> +
> + return irq_find_matching_fwnode(iort_fwnode, DOMAIN_BUS_PLATFORM_MSI);
> +}
> +
> +void acpi_configure_pmsi_domain(struct device *dev)
> +{
> + struct irq_domain *msi_domain;
> +
> + msi_domain = iort_get_platform_device_domain(dev);
> + if (msi_domain)
> + dev_set_msi_domain(dev, msi_domain);
> +}
> +
> static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
> {
> u32 *rid = data;
> diff --git a/drivers/base/platform.c b/drivers/base/platform.c
> index c4af003..3e68f31 100644
> --- a/drivers/base/platform.c
> +++ b/drivers/base/platform.c
> @@ -537,6 +537,9 @@ struct platform_device *platform_device_register_full(
> goto err;
> }
>
> + if (pdevinfo->pre_add_cb)
> + pdevinfo->pre_add_cb(&pdev->dev);
> +
-> because it looks like this might be done in acpi_platform_notify()
for platform devices.
> ret = platform_device_add(pdev);
> if (ret) {
Thanks,
Rafael
^ permalink raw reply
* [PATCH 1/2] arm64: setup: introduce kaslr_offset()
From: Alexander Popov @ 2016-12-22 12:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161222061857.GA26502@yury-N73SV>
On 22.12.2016 09:18, Yury Norov wrote:
> On Sun, Dec 11, 2016 at 03:50:55AM +0300, Alexander Popov wrote:
>> Introduce kaslr_offset() similarly to x86_64 for fixing kcov.
[...]
> Hi Alexander,
>
> I found today's linux-next master broken:
[...]
> It looks like you declare kaslr_offset() twice - in this patch, and in 7ede8665f
> (arm64: setup: introduce kaslr_offset()).
Hello Yury,
There was a race during applying this patch. So currently linux-next has 2 versions of it.
The first one is 1a339a14b1f2c7a0dfdd6db79eee1e55d3cec357, which is original.
The second one is 7ede8665f27cde7da69e8b2fbeaa1ed0664879c5, updated by Will Deacon and
applied to the mainline.
I'm sorry for that. The first one should be definitely dropped.
Best regards,
Alexander
^ permalink raw reply
* [PATCH v4 06/12] dt: bindings: Add bindings for Marvell Xenon SD Host Controller
From: Ziji Hu @ 2016-12-22 12:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161222111802.GX14217@n2100.armlinux.org.uk>
Hi Russell,
On 2016/12/22 19:18, Russell King - ARM Linux wrote:
> On Tue, Dec 13, 2016 at 06:48:35PM +0100, Gregory CLEMENT wrote:
>> +Optional Properties:
>> +- mmc-card:
>> + mmc-card child node must be provided when current SDHC is for eMMC.
>> + Xenon SDHC often can support both SD and eMMC. This child node indicates that
>> + current SDHC is for eMMC card. Thus Xenon eMMC specific configuration and
>> + operations can be enabled prior to eMMC init sequence.
>> + Please refer to Documentation/devicetree/bindings/mmc/mmc-card.txt.
>> + This child node should not be set if current Xenon SDHC is for SD/SDIO.
>
> This looks like a typo - shouldn't it be "mmccard" and not "mmc-card"?
> Your examples below use "mmccard" as does the documentation you point
> towards.
>
Thanks a lot for the review.
I might mix up it with compatible "mmc-card".
I will change it to sub-node name in next version soon.
Thank you.
Best regards,
Hu Ziji
^ permalink raw reply
* [RFC v4 15/16] vfio/type1: Check MSI remapping at irq domain level
From: Diana Madalina Craciun @ 2016-12-22 12:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481661034-3088-16-git-send-email-eric.auger@redhat.com>
Hi Eric,
On 12/13/2016 10:32 PM, Eric Auger wrote:
> In case the IOMMU does not bypass MSI transactions (typical
> case on ARM), we check all MSI controllers are IRQ remapping
> capable. If not the IRQ assignment may be unsafe.
>
> At this stage the arm-smmu-(v3) still advertise the
> IOMMU_CAP_INTR_REMAP capability at IOMMU level. This will be
> removed in subsequent patches.
>
> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> ---
> drivers/vfio/vfio_iommu_type1.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
> index d07fe73..a05648b 100644
> --- a/drivers/vfio/vfio_iommu_type1.c
> +++ b/drivers/vfio/vfio_iommu_type1.c
> @@ -37,6 +37,7 @@
> #include <linux/vfio.h>
> #include <linux/workqueue.h>
> #include <linux/dma-iommu.h>
> +#include <linux/irqdomain.h>
>
> #define DRIVER_VERSION "0.2"
> #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
> @@ -765,7 +766,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
> struct vfio_domain *domain, *d;
> struct bus_type *bus = NULL;
> int ret;
> - bool resv_msi;
> + bool resv_msi, msi_remap;
> phys_addr_t resv_msi_base;
>
> mutex_lock(&iommu->lock);
> @@ -818,8 +819,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
> INIT_LIST_HEAD(&domain->group_list);
> list_add(&group->next, &domain->group_list);
>
> - if (!allow_unsafe_interrupts &&
> - !iommu_capable(bus, IOMMU_CAP_INTR_REMAP)) {
> + msi_remap = resv_msi ? irq_domain_check_msi_remap() :
> + iommu_capable(bus, IOMMU_CAP_INTR_REMAP);
> +
> + if (!allow_unsafe_interrupts && !msi_remap) {
> pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
> __func__);
> ret = -EPERM;
I tested your v4.9-reserved-v4 branch on a ITS capable hardware (NXP
LS2080), so I did not set allow_unsafe_interrupts. It fails here
complaining that the there is no interrupt remapping support. The
irq_domain_check_msi_remap function returns false as none of the checked
domains has the IRQ_DOMAIN_FLAG_MSI_REMAP flag set. I think the reason
is that the flags are not propagated through the domain hierarchy when
the domain is created.
Thanks,
Diana
^ permalink raw reply
* [PATCH 3/3] dmaengine: pl330: Don't require irq-safe runtime PM
From: Marek Szyprowski @ 2016-12-22 12:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482408689-21971-1-git-send-email-m.szyprowski@samsung.com>
This patch replaces irq-safe runtime pm with non-irq-safe version.
Till now non-irq-safe runtime PM implementation was only possible by calling
pm_runtime_get/put functions from alloc/free_chan_resources. All other DMA
engine API functions cannot be called from a context, which allows sleeping.
Such implementation in practice resulted in keeping PL330 DMA controller
device active almost all the time, because most of the slave device drivers
(DMA engine clients) acquired DMA channel in their probe() function and
released it during driver removal.
This patch provides a new approach. The main assumption for it is an
observation that there can be only one slave device using each DMA channel.
Using recently introduced device dependencies (links) infrastructure one can
ensure proper runtime PM state of PL330 DMA controller. In this approach in
pl330_alloc_chan_resources() function a new dependency is being created
between PL330 DMA controller device (as supplier) and given slave device
(as consumer). This way PL330 DMA controller device runtime active counter
is increased when the slave device is resumed and decreased the same time
when given slave device is put to suspend. This way it has been ensured to
keep PL330 DMA controller runtime active if there is an active used of any
of its DMA channels. Slave device pointer is initially stored in per-channel
data in of_dma_xlate callback. This is similar to what has been already
implemented in Exynos IOMMU driver in commit 2f5f44f205cc958b
("iommu/exynos: Use device dependency links to control runtime pm").
If one requests memory-to-memory chanel, runtime active counter is
increased unconditionally. This might be a drawback of this approach, but
PL330 is not really used for memory-to-memory operations due to poor
performance in such operations compared to CPU.
Introducing non-irq-safe runtime power management finally allows to turn
audio power domain off on Exynos5 SoCs.
Removal of irq-safe runtime pm is based on the revert of the following
commits:
1. "dmaengine: pl330: fix runtime pm support" commit <no stable id yet>
2. "dmaengine: pl330: Fix hang on dmaengine_terminate_all on certain boards"
commit 81cc6edc08705ac0146fe6ac14a0982a31ce6f3d
3. "ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12"
commit ae43b3289186480f81c78bb63d788a85a3631f47
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
drivers/dma/pl330.c | 133 +++++++++++++++++++++++++++-------------------------
1 file changed, 70 insertions(+), 63 deletions(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 3c80e71..2cffbb4 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -268,9 +268,6 @@ enum pl330_byteswap {
#define NR_DEFAULT_DESC 16
-/* Delay for runtime PM autosuspend, ms */
-#define PL330_AUTOSUSPEND_DELAY 20
-
/* Populated by the PL330 core driver for DMA API driver's info */
struct pl330_config {
u32 periph_id;
@@ -449,7 +446,8 @@ struct dma_pl330_chan {
bool cyclic;
/* for runtime pm tracking */
- bool active;
+ struct device *slave;
+ struct device_link *slave_link;
};
struct pl330_dmac {
@@ -2015,7 +2013,6 @@ static void pl330_tasklet(unsigned long data)
struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
struct dma_pl330_desc *desc, *_dt;
unsigned long flags;
- bool power_down = false;
spin_lock_irqsave(&pch->lock, flags);
@@ -2030,18 +2027,10 @@ static void pl330_tasklet(unsigned long data)
/* Try to submit a req imm. next to the last completed cookie */
fill_queue(pch);
- if (list_empty(&pch->work_list)) {
- spin_lock(&pch->thread->dmac->lock);
- _stop(pch->thread);
- spin_unlock(&pch->thread->dmac->lock);
- power_down = true;
- pch->active = false;
- } else {
- /* Make sure the PL330 Channel thread is active */
- spin_lock(&pch->thread->dmac->lock);
- _start(pch->thread);
- spin_unlock(&pch->thread->dmac->lock);
- }
+ /* Make sure the PL330 Channel thread is active */
+ spin_lock(&pch->thread->dmac->lock);
+ _start(pch->thread);
+ spin_unlock(&pch->thread->dmac->lock);
while (!list_empty(&pch->completed_list)) {
struct dmaengine_desc_callback cb;
@@ -2054,13 +2043,6 @@ static void pl330_tasklet(unsigned long data)
if (pch->cyclic) {
desc->status = PREP;
list_move_tail(&desc->node, &pch->work_list);
- if (power_down) {
- pch->active = true;
- spin_lock(&pch->thread->dmac->lock);
- _start(pch->thread);
- spin_unlock(&pch->thread->dmac->lock);
- power_down = false;
- }
} else {
desc->status = FREE;
list_move_tail(&desc->node, &pch->dmac->desc_pool);
@@ -2075,12 +2057,6 @@ static void pl330_tasklet(unsigned long data)
}
}
spin_unlock_irqrestore(&pch->lock, flags);
-
- /* If work list empty, power down */
- if (power_down) {
- pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
- pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
- }
}
bool pl330_filter(struct dma_chan *chan, void *param)
@@ -2113,14 +2089,63 @@ static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec,
if (chan_id >= pl330->num_peripherals)
return NULL;
+ if (!pl330->peripherals[chan_id].slave)
+ pl330->peripherals[chan_id].slave = slave;
+ else if (pl330->peripherals[chan_id].slave != slave) {
+ dev_err(pl330->ddma.dev,
+ "Can't use same channel with multiple slave devices!\n");
+ return NULL;
+ }
+
return dma_get_slave_channel(&pl330->peripherals[chan_id].chan);
}
+static int pl330_add_slave_link(struct pl330_dmac *pl330,
+ struct dma_pl330_chan *pch)
+{
+ struct device_link *link;
+ int i;
+
+ if (pch->slave_link)
+ return 0;
+
+ link = device_link_add(pch->slave, pl330->ddma.dev,
+ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+ if (!link)
+ return -ENODEV;
+
+ for (i = 0; i < pl330->num_peripherals; i++)
+ if (pl330->peripherals[i].slave == pch->slave)
+ pl330->peripherals[i].slave_link = link;
+ return 0;
+}
+
+static void pl330_del_slave_link(struct pl330_dmac *pl330,
+ struct dma_pl330_chan *pch)
+{
+ struct device_link *link = pch->slave_link;
+ int i, count = 0;
+
+ for (i = 0; i < pl330->num_peripherals; i++)
+ if (pl330->peripherals[i].slave == pch->slave &&
+ pl330->peripherals[i].thread)
+ count++;
+
+ if (count > 0)
+ return;
+
+ device_link_del(link);
+ for (i = 0; i < pl330->num_peripherals; i++)
+ if (pl330->peripherals[i].slave == pch->slave)
+ pch->slave_link = NULL;
+}
+
static int pl330_alloc_chan_resources(struct dma_chan *chan)
{
struct dma_pl330_chan *pch = to_pchan(chan);
struct pl330_dmac *pl330 = pch->dmac;
unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&pch->lock, flags);
@@ -2137,6 +2162,14 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
spin_unlock_irqrestore(&pch->lock, flags);
+ if (pch->slave)
+ ret = pl330_add_slave_link(pl330, pch);
+ else
+ ret = pm_runtime_get_sync(pl330->ddma.dev);
+
+ if (ret < 0)
+ return ret;
+
return 1;
}
@@ -2171,9 +2204,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
unsigned long flags;
struct pl330_dmac *pl330 = pch->dmac;
LIST_HEAD(list);
- bool power_down = false;
- pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
spin_lock(&pl330->lock);
_stop(pch->thread);
@@ -2182,8 +2213,6 @@ static int pl330_terminate_all(struct dma_chan *chan)
pch->thread->req[0].desc = NULL;
pch->thread->req[1].desc = NULL;
pch->thread->req_running = -1;
- power_down = pch->active;
- pch->active = false;
/* Mark all desc done */
list_for_each_entry(desc, &pch->submitted_list, node) {
@@ -2200,10 +2229,6 @@ static int pl330_terminate_all(struct dma_chan *chan)
list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
- pm_runtime_mark_last_busy(pl330->ddma.dev);
- if (power_down)
- pm_runtime_put_autosuspend(pl330->ddma.dev);
- pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
}
@@ -2221,7 +2246,6 @@ static int pl330_pause(struct dma_chan *chan)
struct pl330_dmac *pl330 = pch->dmac;
unsigned long flags;
- pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
spin_lock(&pl330->lock);
@@ -2229,8 +2253,6 @@ static int pl330_pause(struct dma_chan *chan)
spin_unlock(&pl330->lock);
spin_unlock_irqrestore(&pch->lock, flags);
- pm_runtime_mark_last_busy(pl330->ddma.dev);
- pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
}
@@ -2238,11 +2260,11 @@ static int pl330_pause(struct dma_chan *chan)
static void pl330_free_chan_resources(struct dma_chan *chan)
{
struct dma_pl330_chan *pch = to_pchan(chan);
+ struct pl330_dmac *pl330 = pch->dmac;
unsigned long flags;
tasklet_kill(&pch->task);
- pm_runtime_get_sync(pch->dmac->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
pl330_release_channel(pch->thread);
@@ -2252,19 +2274,20 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
- pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
- pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
+
+ if (pch->slave)
+ pl330_del_slave_link(pl330, pch);
+ else
+ pm_runtime_put(pl330->ddma.dev);
}
static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
struct dma_pl330_desc *desc)
{
struct pl330_thread *thrd = pch->thread;
- struct pl330_dmac *pl330 = pch->dmac;
void __iomem *regs = thrd->dmac->base;
u32 val, addr;
- pm_runtime_get_sync(pl330->ddma.dev);
val = addr = 0;
if (desc->rqcfg.src_inc) {
val = readl(regs + SA(thrd->id));
@@ -2273,8 +2296,6 @@ static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
val = readl(regs + DA(thrd->id));
addr = desc->px.dst_addr;
}
- pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
- pm_runtime_put_autosuspend(pl330->ddma.dev);
/* If DMAMOV hasn't finished yet, SAR/DAR can be zero */
if (!val)
@@ -2360,16 +2381,6 @@ static void pl330_issue_pending(struct dma_chan *chan)
unsigned long flags;
spin_lock_irqsave(&pch->lock, flags);
- if (list_empty(&pch->work_list)) {
- /*
- * Warn on nothing pending. Empty submitted_list may
- * break our pm_runtime usage counter as it is
- * updated on work_list emptiness status.
- */
- WARN_ON(list_empty(&pch->submitted_list));
- pch->active = true;
- pm_runtime_get_sync(pch->dmac->ddma.dev);
- }
list_splice_tail_init(&pch->submitted_list, &pch->work_list);
spin_unlock_irqrestore(&pch->lock, flags);
@@ -2987,11 +2998,7 @@ static int __maybe_unused pl330_resume(struct device *dev)
pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan,
pcfg->num_peri, pcfg->num_events);
- pm_runtime_irq_safe(&adev->dev);
- pm_runtime_use_autosuspend(&adev->dev);
- pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY);
- pm_runtime_mark_last_busy(&adev->dev);
- pm_runtime_put_autosuspend(&adev->dev);
+ pm_runtime_put(&adev->dev);
return 0;
probe_err3:
--
1.9.1
^ permalink raw reply related
* [PATCH 2/3] dmaengine: Forward slave device pointer to of_xlate callback
From: Marek Szyprowski @ 2016-12-22 12:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482408689-21971-1-git-send-email-m.szyprowski@samsung.com>
Add pointer to slave struct device to of_dma_xlate to let DMA engine
driver to know which slave device is using given DMA channel. This
will be later used to implement non-irqsafe runtime PM for DMA engine
driver.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
drivers/dma/amba-pl08x.c | 2 +-
drivers/dma/at_hdmac.c | 4 ++--
drivers/dma/at_xdmac.c | 2 +-
drivers/dma/bcm2835-dma.c | 2 +-
drivers/dma/coh901318.c | 2 +-
drivers/dma/cppi41.c | 2 +-
drivers/dma/dma-jz4780.c | 2 +-
drivers/dma/dmaengine.c | 2 +-
drivers/dma/dw/platform.c | 2 +-
drivers/dma/edma.c | 4 ++--
drivers/dma/fsl-edma.c | 2 +-
drivers/dma/img-mdc-dma.c | 2 +-
drivers/dma/imx-dma.c | 2 +-
drivers/dma/imx-sdma.c | 2 +-
drivers/dma/k3dma.c | 2 +-
drivers/dma/mmp_pdma.c | 2 +-
drivers/dma/mmp_tdma.c | 2 +-
drivers/dma/moxart-dma.c | 2 +-
drivers/dma/mxs-dma.c | 2 +-
drivers/dma/nbpfaxi.c | 2 +-
drivers/dma/of-dma.c | 20 ++++++++++++--------
drivers/dma/pl330.c | 3 ++-
drivers/dma/pxa_dma.c | 2 +-
drivers/dma/qcom/bam_dma.c | 2 +-
drivers/dma/sh/rcar-dmac.c | 2 +-
drivers/dma/sh/shdma-of.c | 2 +-
drivers/dma/sh/usb-dmac.c | 2 +-
drivers/dma/sirf-dma.c | 2 +-
drivers/dma/st_fdma.c | 2 +-
drivers/dma/ste_dma40.c | 2 +-
drivers/dma/stm32-dma.c | 2 +-
drivers/dma/sun4i-dma.c | 2 +-
drivers/dma/sun6i-dma.c | 2 +-
drivers/dma/tegra20-apb-dma.c | 2 +-
drivers/dma/tegra210-adma.c | 2 +-
drivers/dma/xilinx/xilinx_dma.c | 2 +-
drivers/dma/xilinx/zynqmp_dma.c | 2 +-
drivers/dma/zx296702_dma.c | 2 +-
include/linux/of_dma.h | 17 +++++++++--------
39 files changed, 61 insertions(+), 55 deletions(-)
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 0b7c6ce..194089c 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -2059,7 +2059,7 @@ static struct dma_chan *pl08x_find_chan_id(struct pl08x_driver_data *pl08x,
}
static struct dma_chan *pl08x_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct pl08x_driver_data *pl08x = ofdma->of_dma_data;
struct dma_chan *dma_chan;
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 1baf340..b228b26 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1788,7 +1788,7 @@ static bool at_dma_filter(struct dma_chan *chan, void *slave)
}
static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *of_dma)
+ struct of_dma *of_dma, struct device *slave)
{
struct dma_chan *chan;
struct at_dma_chan *atchan;
@@ -1847,7 +1847,7 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
}
#else
static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *of_dma)
+ struct of_dma *of_dma, struct device *slave)
{
return NULL;
}
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index 7d4e0bc..9ddd868 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -508,7 +508,7 @@ static inline void at_xdmac_increment_block_count(struct dma_chan *chan,
}
static struct dma_chan *at_xdmac_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *of_dma)
+ struct of_dma *of_dma, struct device *slave)
{
struct at_xdmac *atxdmac = of_dma->of_dma_data;
struct at_xdmac_chan *atchan;
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index e18dc596c..e9c417a 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -877,7 +877,7 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od)
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct bcm2835_dmadev *d = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index 74794c9..dbc4fb4 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -1779,7 +1779,7 @@ static bool coh901318_filter_base_and_id(struct dma_chan *chan, void *data)
}
static struct dma_chan *coh901318_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct coh901318_filter_args args = {
.base = ofdma->of_dma_data,
diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c
index d5ba43a..389a227 100644
--- a/drivers/dma/cppi41.c
+++ b/drivers/dma/cppi41.c
@@ -932,7 +932,7 @@ static bool cpp41_dma_filter_fn(struct dma_chan *chan, void *param)
};
static struct dma_chan *cppi41_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
int count = dma_spec->args_count;
struct of_dma_filter_info *info = ofdma->of_dma_data;
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 7373b7a..f65f716 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -707,7 +707,7 @@ static bool jz4780_dma_filter_fn(struct dma_chan *chan, void *param)
}
static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct jz4780_dma_dev *jzdma = ofdma->of_dma_data;
dma_cap_mask_t mask = jzdma->dma_device.cap_mask;
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 6b53526..9204ae8 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -708,7 +708,7 @@ struct dma_chan *dma_request_chan(struct device *dev, const char *name)
/* If device-tree is present get slave info from here */
if (dev->of_node)
- chan = of_dma_request_slave_channel(dev->of_node, name);
+ chan = of_dma_request_slave_channel(dev, name);
/* If device was enumerated by ACPI get slave info from here */
if (has_acpi_companion(dev) && !chan)
diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c
index b1655e4..7567b19 100644
--- a/drivers/dma/dw/platform.c
+++ b/drivers/dma/dw/platform.c
@@ -29,7 +29,7 @@
#define DRV_NAME "dw_dmac"
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct dw_dma *dw = ofdma->of_dma_data;
struct dw_dma_slave slave = {
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index 3879f80..d2e7d89 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -2117,7 +2117,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
}
static struct dma_chan *of_edma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct edma_cc *ecc = ofdma->of_dma_data;
struct dma_chan *chan = NULL;
@@ -2161,7 +2161,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
}
static struct dma_chan *of_edma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
return NULL;
}
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 6775f2c..915aa81 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -750,7 +750,7 @@ static void fsl_edma_issue_pending(struct dma_chan *chan)
}
static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
struct dma_chan *chan, *_chan;
diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c
index 54db141..9a969cb 100644
--- a/drivers/dma/img-mdc-dma.c
+++ b/drivers/dma/img-mdc-dma.c
@@ -793,7 +793,7 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
}
static struct dma_chan *mdc_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct mdc_dma *mdma = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index ab0fb80..b145bab 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -1032,7 +1032,7 @@ static bool imxdma_filter_fn(struct dma_chan *chan, void *param)
}
static struct dma_chan *imxdma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
int count = dma_spec->args_count;
struct imxdma_engine *imxdma = ofdma->of_dma_data;
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index d1651a5..7c3cdb3 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -1659,7 +1659,7 @@ static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param)
}
static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct sdma_engine *sdma = ofdma->of_dma_data;
dma_cap_mask_t mask = sdma->dma_device.cap_mask;
diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
index 01e25c6..dd0e7fe 100644
--- a/drivers/dma/k3dma.c
+++ b/drivers/dma/k3dma.c
@@ -786,7 +786,7 @@ static int k3_dma_transfer_resume(struct dma_chan *chan)
MODULE_DEVICE_TABLE(of, k3_pdma_dt_ids);
static struct dma_chan *k3_of_dma_simple_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct k3_dma_dev *d = ofdma->of_dma_data;
unsigned int request = dma_spec->args[0];
diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c
index eb3a1f4..569ec8f 100644
--- a/drivers/dma/mmp_pdma.c
+++ b/drivers/dma/mmp_pdma.c
@@ -993,7 +993,7 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq)
MODULE_DEVICE_TABLE(of, mmp_pdma_dt_ids);
static struct dma_chan *mmp_pdma_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct mmp_pdma_device *d = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
index 13c68b6..ca56e73 100644
--- a/drivers/dma/mmp_tdma.c
+++ b/drivers/dma/mmp_tdma.c
@@ -591,7 +591,7 @@ static bool mmp_tdma_filter_fn(struct dma_chan *chan, void *fn_param)
}
static struct dma_chan *mmp_tdma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct mmp_tdma_device *tdev = ofdma->of_dma_data;
dma_cap_mask_t mask = tdev->device.cap_mask;
diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c
index e1a5c22..d7c32a3 100644
--- a/drivers/dma/moxart-dma.c
+++ b/drivers/dma/moxart-dma.c
@@ -330,7 +330,7 @@ static struct dma_async_tx_descriptor *moxart_prep_slave_sg(
}
static struct dma_chan *moxart_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct moxart_dmadev *mdc = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index e217268..3cc0e6b 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -747,7 +747,7 @@ static bool mxs_dma_filter_fn(struct dma_chan *chan, void *fn_param)
}
static struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct mxs_dma_engine *mxs_dma = ofdma->of_dma_data;
dma_cap_mask_t mask = mxs_dma->dma_device.cap_mask;
diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c
index 3f45b9b..cb6a981 100644
--- a/drivers/dma/nbpfaxi.c
+++ b/drivers/dma/nbpfaxi.c
@@ -1096,7 +1096,7 @@ static void nbpf_free_chan_resources(struct dma_chan *dchan)
}
static struct dma_chan *nbpf_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct nbpf_device *nbpf = ofdma->of_dma_data;
struct dma_chan *dchan;
diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c
index faae0bf..b6fd9e1 100644
--- a/drivers/dma/of-dma.c
+++ b/drivers/dma/of-dma.c
@@ -54,7 +54,8 @@ static struct of_dma *of_dma_find_controller(struct of_phandle_args *dma_spec)
* to request channel from the real DMA controller.
*/
static struct dma_chan *of_dma_router_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma,
+ struct device *slave)
{
struct dma_chan *chan;
struct of_dma *ofdma_target;
@@ -71,7 +72,8 @@ static struct dma_chan *of_dma_router_xlate(struct of_phandle_args *dma_spec,
if (!ofdma_target)
return NULL;
- chan = ofdma_target->of_dma_xlate(&dma_spec_target, ofdma_target);
+ chan = ofdma_target->of_dma_xlate(&dma_spec_target, ofdma_target,
+ slave);
if (chan) {
chan->router = ofdma->dma_router;
chan->route_data = route_data;
@@ -103,7 +105,8 @@ static struct dma_chan *of_dma_router_xlate(struct of_phandle_args *dma_spec,
*/
int of_dma_controller_register(struct device_node *np,
struct dma_chan *(*of_dma_xlate)
- (struct of_phandle_args *, struct of_dma *),
+ (struct of_phandle_args *, struct of_dma *,
+ struct device *),
void *data)
{
struct of_dma *ofdma;
@@ -229,14 +232,15 @@ static int of_dma_match_channel(struct device_node *np, const char *name,
/**
* of_dma_request_slave_channel - Get the DMA slave channel
- * @np: device node to get DMA request from
+ * @slave: device to get DMA request from
* @name: name of desired channel
*
* Returns pointer to appropriate DMA channel on success or an error pointer.
*/
-struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
+struct dma_chan *of_dma_request_slave_channel(struct device *slave,
const char *name)
{
+ struct device_node *np = slave->of_node;
struct of_phandle_args dma_spec;
struct of_dma *ofdma;
struct dma_chan *chan;
@@ -275,7 +279,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
ofdma = of_dma_find_controller(&dma_spec);
if (ofdma) {
- chan = ofdma->of_dma_xlate(&dma_spec, ofdma);
+ chan = ofdma->of_dma_xlate(&dma_spec, ofdma, slave);
} else {
ret_no_channel = -EPROBE_DEFER;
chan = NULL;
@@ -305,7 +309,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
* pointer to appropriate dma channel on success or NULL on error.
*/
struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
int count = dma_spec->args_count;
struct of_dma_filter_info *info = ofdma->of_dma_data;
@@ -335,7 +339,7 @@ struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
* Returns pointer to appropriate dma channel on success or NULL on error.
*/
struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct dma_device *dev = ofdma->of_dma_data;
struct dma_chan *chan, *candidate = NULL;
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 27cc5d2..3c80e71 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2096,7 +2096,8 @@ bool pl330_filter(struct dma_chan *chan, void *param)
EXPORT_SYMBOL(pl330_filter);
static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma,
+ struct device *slave)
{
int count = dma_spec->args_count;
struct pl330_dmac *pl330 = ofdma->of_dma_data;
diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c
index b53fb61..434764b 100644
--- a/drivers/dma/pxa_dma.c
+++ b/drivers/dma/pxa_dma.c
@@ -1336,7 +1336,7 @@ static int pxad_init_phys(struct platform_device *op,
MODULE_DEVICE_TABLE(of, pxad_dt_ids);
static struct dma_chan *pxad_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct pxad_device *d = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index 03c4eb3..7ff3075 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -1049,7 +1049,7 @@ static void bam_dma_free_desc(struct virt_dma_desc *vd)
}
static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *of)
+ struct of_dma *of, struct device *slave)
{
struct bam_device *bdev = container_of(of->of_dma_data,
struct bam_device, common);
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index 2e441d0..7cecf03 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -1552,7 +1552,7 @@ static bool rcar_dmac_chan_filter(struct dma_chan *chan, void *arg)
}
static struct dma_chan *rcar_dmac_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct rcar_dmac_chan *rchan;
struct dma_chan *chan;
diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c
index f999f9b..9953be9 100644
--- a/drivers/dma/sh/shdma-of.c
+++ b/drivers/dma/sh/shdma-of.c
@@ -20,7 +20,7 @@
#define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan)
static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
u32 id = dma_spec->args[0];
dma_cap_mask_t mask;
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index 72c6497..0f06a94 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -650,7 +650,7 @@ static bool usb_dmac_chan_filter(struct dma_chan *chan, void *arg)
}
static struct dma_chan *usb_dmac_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct dma_chan *chan;
dma_cap_mask_t mask;
diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
index a0733ac3e..ed6f07b 100644
--- a/drivers/dma/sirf-dma.c
+++ b/drivers/dma/sirf-dma.c
@@ -826,7 +826,7 @@ bool sirfsoc_dma_filter_id(struct dma_chan *chan, void *chan_id)
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
static struct dma_chan *of_dma_sirfsoc_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct sirfsoc_dma *sdma = ofdma->of_dma_data;
unsigned int request = dma_spec->args[0];
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
index bfb79bd..6a92a65 100644
--- a/drivers/dma/st_fdma.c
+++ b/drivers/dma/st_fdma.c
@@ -167,7 +167,7 @@ static irqreturn_t st_fdma_irq_handler(int irq, void *dev_id)
}
static struct dma_chan *st_fdma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct st_fdma_dev *fdev = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 8684d11..d7b0662 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -2334,7 +2334,7 @@ static void d40_set_prio_realtime(struct d40_chan *d40c)
#define D40_DT_FLAGS_HIGH_PRIO(flags) ((flags >> 4) & 0x1)
static struct dma_chan *d40_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct stedma40_chan_cfg cfg;
dma_cap_mask_t cap;
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 3688d08..8e8e592 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -965,7 +965,7 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan,
}
static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct stm32_dma_device *dmadev = ofdma->of_dma_data;
struct stm32_dma_cfg cfg;
diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c
index 57aa227..c1a0763 100644
--- a/drivers/dma/sun4i-dma.c
+++ b/drivers/dma/sun4i-dma.c
@@ -909,7 +909,7 @@ static int sun4i_dma_config(struct dma_chan *chan,
}
static struct dma_chan *sun4i_dma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct sun4i_dma_dev *priv = ofdma->of_dma_data;
struct sun4i_dma_vchan *vchan;
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index a235878..240e4a9 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -930,7 +930,7 @@ static void sun6i_dma_free_chan_resources(struct dma_chan *chan)
}
static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct sun6i_dma_dev *sdev = ofdma->of_dma_data;
struct sun6i_vchan *vchan;
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index 3722b9d..e0eb581 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -1239,7 +1239,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
}
static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct tegra_dma *tdma = ofdma->of_dma_data;
struct dma_chan *chan;
diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c
index b10cbaa..525af32 100644
--- a/drivers/dma/tegra210-adma.c
+++ b/drivers/dma/tegra210-adma.c
@@ -605,7 +605,7 @@ static void tegra_adma_free_chan_resources(struct dma_chan *dc)
}
static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct tegra_adma *tdma = ofdma->of_dma_data;
struct tegra_adma_chan *tdc;
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 8288fe4..69cdfc3 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -2461,7 +2461,7 @@ static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev,
* Return: DMA channel pointer on success and NULL on error
*/
static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct xilinx_dma_device *xdev = ofdma->of_dma_data;
int chan_id = dma_spec->args[0];
diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index 6d221e5..6aa133c 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -1040,7 +1040,7 @@ static int zynqmp_dma_chan_probe(struct zynqmp_dma_device *zdev,
* Return: DMA channel pointer on success and NULL on error
*/
static struct dma_chan *of_zynqmp_dma_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct zynqmp_dma_device *zdev = ofdma->of_dma_data;
diff --git a/drivers/dma/zx296702_dma.c b/drivers/dma/zx296702_dma.c
index 380276d..a07f1c1 100644
--- a/drivers/dma/zx296702_dma.c
+++ b/drivers/dma/zx296702_dma.c
@@ -731,7 +731,7 @@ static void zx_dma_free_desc(struct virt_dma_desc *vd)
MODULE_DEVICE_TABLE(of, zx6702_dma_dt_ids);
static struct dma_chan *zx_of_dma_simple_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
struct zx_dma_dev *d = ofdma->of_dma_data;
unsigned int request = dma_spec->args[0];
diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h
index b90d8ec..a0a6c8c 100644
--- a/include/linux/of_dma.h
+++ b/include/linux/of_dma.h
@@ -22,7 +22,8 @@ struct of_dma {
struct list_head of_dma_controllers;
struct device_node *of_node;
struct dma_chan *(*of_dma_xlate)
- (struct of_phandle_args *, struct of_dma *);
+ (struct of_phandle_args *, struct of_dma *,
+ struct device *);
void *(*of_dma_route_allocate)
(struct of_phandle_args *, struct of_dma *);
struct dma_router *dma_router;
@@ -37,7 +38,7 @@ struct of_dma_filter_info {
#ifdef CONFIG_DMA_OF
extern int of_dma_controller_register(struct device_node *np,
struct dma_chan *(*of_dma_xlate)
- (struct of_phandle_args *, struct of_dma *),
+ (struct of_phandle_args *, struct of_dma *, struct device *),
void *data);
extern void of_dma_controller_free(struct device_node *np);
@@ -47,17 +48,17 @@ extern int of_dma_router_register(struct device_node *np,
struct dma_router *dma_router);
#define of_dma_router_free of_dma_controller_free
-extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
+extern struct dma_chan *of_dma_request_slave_channel(struct device *slave,
const char *name);
extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma);
+ struct of_dma *ofdma, struct device *slave);
extern struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma);
+ struct of_dma *ofdma, struct device *slave);
#else
static inline int of_dma_controller_register(struct device_node *np,
struct dma_chan *(*of_dma_xlate)
- (struct of_phandle_args *, struct of_dma *),
+ (struct of_phandle_args *, struct of_dma *, struct device *),
void *data)
{
return -ENODEV;
@@ -77,14 +78,14 @@ static inline int of_dma_router_register(struct device_node *np,
#define of_dma_router_free of_dma_controller_free
-static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
+static inline struct dma_chan *of_dma_request_slave_channel(struct device *slave,
const char *name)
{
return ERR_PTR(-ENODEV);
}
static inline struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
- struct of_dma *ofdma)
+ struct of_dma *ofdma, struct device *slave)
{
return NULL;
}
--
1.9.1
^ permalink raw reply related
* [PATCH 1/3] dmaengine: pl330: remove pdata based initialization
From: Marek Szyprowski @ 2016-12-22 12:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482408689-21971-1-git-send-email-m.szyprowski@samsung.com>
This driver is now used only on platforms which supports device tree, so
it is safe to remove legacy platform data based initialization code.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
drivers/dma/pl330.c | 30 ++++++++----------------------
include/linux/amba/pl330.h | 35 -----------------------------------
2 files changed, 8 insertions(+), 57 deletions(-)
delete mode 100644 include/linux/amba/pl330.h
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 740bbb9..27cc5d2 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -22,7 +22,6 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/amba/bus.h>
-#include <linux/amba/pl330.h>
#include <linux/scatterlist.h>
#include <linux/of.h>
#include <linux/of_dma.h>
@@ -2839,7 +2838,6 @@ static int __maybe_unused pl330_resume(struct device *dev)
static int
pl330_probe(struct amba_device *adev, const struct amba_id *id)
{
- struct dma_pl330_platdata *pdat;
struct pl330_config *pcfg;
struct pl330_dmac *pl330;
struct dma_pl330_chan *pch, *_p;
@@ -2849,8 +2847,6 @@ static int __maybe_unused pl330_resume(struct device *dev)
int num_chan;
struct device_node *np = adev->dev.of_node;
- pdat = dev_get_platdata(&adev->dev);
-
ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
@@ -2863,7 +2859,7 @@ static int __maybe_unused pl330_resume(struct device *dev)
pd = &pl330->ddma;
pd->dev = &adev->dev;
- pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
+ pl330->mcbufsz = 0;
/* get quirk */
for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
@@ -2907,10 +2903,7 @@ static int __maybe_unused pl330_resume(struct device *dev)
INIT_LIST_HEAD(&pd->channels);
/* Initialize channel parameters */
- if (pdat)
- num_chan = max_t(int, pdat->nr_valid_peri, pcfg->num_chan);
- else
- num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan);
+ num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan);
pl330->num_peripherals = num_chan;
@@ -2922,11 +2915,8 @@ static int __maybe_unused pl330_resume(struct device *dev)
for (i = 0; i < num_chan; i++) {
pch = &pl330->peripherals[i];
- if (!adev->dev.of_node)
- pch->chan.private = pdat ? &pdat->peri_id[i] : NULL;
- else
- pch->chan.private = adev->dev.of_node;
+ pch->chan.private = adev->dev.of_node;
INIT_LIST_HEAD(&pch->submitted_list);
INIT_LIST_HEAD(&pch->work_list);
INIT_LIST_HEAD(&pch->completed_list);
@@ -2939,15 +2929,11 @@ static int __maybe_unused pl330_resume(struct device *dev)
list_add_tail(&pch->chan.device_node, &pd->channels);
}
- if (pdat) {
- pd->cap_mask = pdat->cap_mask;
- } else {
- dma_cap_set(DMA_MEMCPY, pd->cap_mask);
- if (pcfg->num_peri) {
- dma_cap_set(DMA_SLAVE, pd->cap_mask);
- dma_cap_set(DMA_CYCLIC, pd->cap_mask);
- dma_cap_set(DMA_PRIVATE, pd->cap_mask);
- }
+ dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+ if (pcfg->num_peri) {
+ dma_cap_set(DMA_SLAVE, pd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, pd->cap_mask);
+ dma_cap_set(DMA_PRIVATE, pd->cap_mask);
}
pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h
deleted file mode 100644
index fe93758..0000000
--- a/include/linux/amba/pl330.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* linux/include/linux/amba/pl330.h
- *
- * Copyright (C) 2010 Samsung Electronics Co. Ltd.
- * Jaswinder Singh <jassi.brar@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __AMBA_PL330_H_
-#define __AMBA_PL330_H_
-
-#include <linux/dmaengine.h>
-
-struct dma_pl330_platdata {
- /*
- * Number of valid peripherals connected to DMAC.
- * This may be different from the value read from
- * CR0, as the PL330 implementation might have 'holes'
- * in the peri list or the peri could also be reached
- * from another DMAC which the platform prefers.
- */
- u8 nr_valid_peri;
- /* Array of valid peripherals */
- u8 *peri_id;
- /* Operational capabilities */
- dma_cap_mask_t cap_mask;
- /* Bytes to allocate for MC buffer */
- unsigned mcbuf_sz;
-};
-
-extern bool pl330_filter(struct dma_chan *chan, void *param);
-#endif /* __AMBA_PL330_H_ */
--
1.9.1
^ permalink raw reply related
* [PATCH 0/3] DMA Engine: switch PL330 driver to non-irq-safe runtime PM
From: Marek Szyprowski @ 2016-12-22 12:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CGME20161222121148epcas5p11d4e49a3724e375a989aa173f731346f@epcas5p1.samsung.com>
Hello,
This patchset changes the way the runtime PM is implemented in PL330 DMA
engine driver. The main goal of such change is to add support for audio
power domain to Exynos5 SoCs (5250, 542x, 5433, probably others) and let
it to be properly turned off, when no audio is being used. Switching to
non-irq-safe runtime PM is needed to properly let power domain to be
turned off (irqsafe runtime PM keeps power domain turned on all the time)
and to integrate with clock controller's runtime PM (this cannot be
workarounded any other way, PL330 uses clocks from the controller, which
belongs to the same power domain).
For more details of the proposed change to PL330 driver see patch #3.
Audio power domain on Exynos5 SoCs contains following hardware modules:
1. clock controller
2. pin controller
3. PL330 DMA controller
4. I2S audio controller
Patches for adding/fixing runtime PM for each of the above device will be
handled separately.
Runtime PM patches for clock controllers is possible and has been proposed
in the following thread (pending review, patch for runtime PM for Exynos
Audio subsystem will be added in v4 soon):
https://www.spinics.net/lists/arm-kernel/msg538122.html
Runtime PM support for Exynos pin controller will be posted soon in
a separate thread.
Exynos I2S driver already supports runtime PM, but some fixes are needed
for it and they will be also posted soon.
Patches are based on linux-next with my PL330 runtime PM fix patch
applied (which I assume will be merged as a fix to v4.10):
https://patchwork.kernel.org/patch/9477695/
Best regards
Marek Szyprowski
Samsung R&D Institute Poland
Patch summary:
Marek Szyprowski (3):
dmaengine: pl330: remove pdata based initialization
dmaengine: Forward slave device pointer to of_xlate callback
dmaengine: pl330: Don't require irq-safe runtime PM
drivers/dma/amba-pl08x.c | 2 +-
drivers/dma/at_hdmac.c | 4 +-
drivers/dma/at_xdmac.c | 2 +-
drivers/dma/bcm2835-dma.c | 2 +-
drivers/dma/coh901318.c | 2 +-
drivers/dma/cppi41.c | 2 +-
drivers/dma/dma-jz4780.c | 2 +-
drivers/dma/dmaengine.c | 2 +-
drivers/dma/dw/platform.c | 2 +-
drivers/dma/edma.c | 4 +-
drivers/dma/fsl-edma.c | 2 +-
drivers/dma/img-mdc-dma.c | 2 +-
drivers/dma/imx-dma.c | 2 +-
drivers/dma/imx-sdma.c | 2 +-
drivers/dma/k3dma.c | 2 +-
drivers/dma/mmp_pdma.c | 2 +-
drivers/dma/mmp_tdma.c | 2 +-
drivers/dma/moxart-dma.c | 2 +-
drivers/dma/mxs-dma.c | 2 +-
drivers/dma/nbpfaxi.c | 2 +-
drivers/dma/of-dma.c | 20 +++--
drivers/dma/pl330.c | 166 +++++++++++++++++++---------------------
drivers/dma/pxa_dma.c | 2 +-
drivers/dma/qcom/bam_dma.c | 2 +-
drivers/dma/sh/rcar-dmac.c | 2 +-
drivers/dma/sh/shdma-of.c | 2 +-
drivers/dma/sh/usb-dmac.c | 2 +-
drivers/dma/sirf-dma.c | 2 +-
drivers/dma/st_fdma.c | 2 +-
drivers/dma/ste_dma40.c | 2 +-
drivers/dma/stm32-dma.c | 2 +-
drivers/dma/sun4i-dma.c | 2 +-
drivers/dma/sun6i-dma.c | 2 +-
drivers/dma/tegra20-apb-dma.c | 2 +-
drivers/dma/tegra210-adma.c | 2 +-
drivers/dma/xilinx/xilinx_dma.c | 2 +-
drivers/dma/xilinx/zynqmp_dma.c | 2 +-
drivers/dma/zx296702_dma.c | 2 +-
include/linux/amba/pl330.h | 35 ---------
include/linux/of_dma.h | 17 ++--
40 files changed, 139 insertions(+), 175 deletions(-)
delete mode 100644 include/linux/amba/pl330.h
--
1.9.1
^ 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