* [PATCH 04/11] drm/bridge/synopsys: dw-hdmi: Export some PHY related functions
From: Laurent Pinchart @ 2018-01-09 13:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171230210203.24115-5-jernej.skrabec@siol.net>
Hi Jernej,
Thank you for the patch.
On Saturday, 30 December 2017 23:01:56 EET Jernej Skrabec wrote:
> Parts of PHY code could be useful also for custom PHYs. For example,
> Allwinner A83T has custom PHY which is probably Synopsys gen2 PHY
> with few additional memory mapped registers, so most of the Synopsys PHY
> related code could be reused.
>
> It turns out that even completely custom HDMI PHYs, such as the one
> found in Allwinner H3, can reuse some of those functions. This would
> suggest that (some?) functions exported in this commit are actually part
> of generic PHY interface and not really specific to Synopsys PHYs.
That's correct, those functions control the interface between the HDMI
controller and the PHY. They're not specific to Synopsys PHYs, but they're
specific to the PHY interface as designed by Synopsys.
> Export useful PHY functions.
>
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 45 ++++++++++++++++++++-------
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 2 ++
> include/drm/bridge/dw_hdmi.h | 10 +++++++
> 3 files changed, 44 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> 7ca14d7325b5..67467d0b683a 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -1037,19 +1037,21 @@ static void dw_hdmi_phy_enable_svsret(struct dw_hdmi
> *hdmi, u8 enable) HDMI_PHY_CONF0_SVSRET_MASK);
> }
>
> -static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
> +void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
> {
> hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
> HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
> HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
> }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_pddq);
>
> -static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
> +void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
> {
> hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
> HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
> HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
> }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_txpwron);
>
> static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
> {
> @@ -1065,6 +1067,23 @@ static void dw_hdmi_phy_sel_interface_control(struct
> dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK);
> }
>
> +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi, u8 enable)
> +{
> + hdmi_mask_writeb(hdmi, enable, HDMI_MC_PHYRSTZ,
> + HDMI_MC_PHYRSTZ_PHYRSTZ_OFFSET,
> + HDMI_MC_PHYRSTZ_PHYRSTZ_MASK);
> +}
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_reset);
> +
> +void dw_hdmi_phy_set_slave_addr(struct dw_hdmi *hdmi)
> +{
> + hdmi_phy_test_clear(hdmi, 1);
> + hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
> + HDMI_PHY_I2CM_SLAVE_ADDR);
> + hdmi_phy_test_clear(hdmi, 0);
> +}
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_set_slave_addr);
Should the I2C address be passed as an argument ?
> static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
> {
> const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
> @@ -1204,15 +1223,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
> dw_hdmi_phy_enable_svsret(hdmi, 1);
>
> /* PHY reset. The reset signal is active high on Gen2 PHYs. */
> - hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
> - hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
> + dw_hdmi_phy_gen2_reset(hdmi, 1);
> + dw_hdmi_phy_gen2_reset(hdmi, 0);
>
> hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
>
> - hdmi_phy_test_clear(hdmi, 1);
> - hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
> - HDMI_PHY_I2CM_SLAVE_ADDR);
> - hdmi_phy_test_clear(hdmi, 0);
> + dw_hdmi_phy_set_slave_addr(hdmi);
>
> /* Write to the PHY as configured by the platform */
> if (pdata->configure_phy)
> @@ -1251,15 +1267,16 @@ static void dw_hdmi_phy_disable(struct dw_hdmi
> *hdmi, void *data) dw_hdmi_phy_power_off(hdmi);
> }
>
> -static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
> - void *data)
> +enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
> + void *data)
> {
> return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
> connector_status_connected : connector_status_disconnected;
> }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_read_hpd);
>
> -static void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
> - bool force, bool disabled, bool rxsense)
> +void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
> + bool force, bool disabled, bool rxsense)
> {
> u8 old_mask = hdmi->phy_mask;
>
> @@ -1271,8 +1288,9 @@ static void dw_hdmi_phy_update_hpd(struct dw_hdmi
> *hdmi, void *data, if (old_mask != hdmi->phy_mask)
> hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
> }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_update_hpd);
>
> -static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
> +void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
> {
> /*
> * Configure the PHY RX SENSE and HPD interrupts polarities and clear
> @@ -1291,6 +1309,7 @@ static void dw_hdmi_phy_setup_hpd(struct dw_hdmi
> *hdmi, void *data) hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
> HDMI_IH_PHY_STAT0_RX_SENSE), HDMI_IH_MUTE_PHY_STAT0);
> }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
>
> static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
> .init = dw_hdmi_phy_init,
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index
> 9d90eb9c46e5..fd150430d0b3 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> @@ -950,6 +950,8 @@ enum {
>
> /* MC_PHYRSTZ field values */
> HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01,
> + HDMI_MC_PHYRSTZ_PHYRSTZ_OFFSET = 0x00,
> + HDMI_MC_PHYRSTZ_PHYRSTZ_MASK = 0x01,
>
> /* MC_HEACPHY_RST field values */
> HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index 182f83283e24..f5cca4362154 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -159,5 +159,15 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi);
> /* PHY configuration */
> void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
> unsigned char addr);
> +enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
> + void *data);
> +void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
> + bool force, bool disabled, bool rxsense);
> +void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data);
> +
> +void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable);
> +void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable);
> +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi, u8 enable);
> +void dw_hdmi_phy_set_slave_addr(struct dw_hdmi *hdmi);
>
> #endif /* __IMX_HDMI_H__ */
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 01/19] drm/fourcc: Add a function to tell if the format embeds alpha
From: Maxime Ripard @ 2018-01-09 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1796930.sWoKI78acY@avalon>
Hi Laurent,
On Tue, Jan 09, 2018 at 02:29:58PM +0200, Laurent Pinchart wrote:
> On Tuesday, 9 January 2018 12:56:20 EET Maxime Ripard wrote:
> > There's a bunch of drivers that duplicate the same function to know if a
> > particular format embeds an alpha component or not.
> >
> > Let's create a helper to avoid duplicating that logic.
> >
> > Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Cc: Eric Anholt <eric@anholt.net>
> > Cc: Inki Dae <inki.dae@samsung.com>
> > Cc: Joonyoung Shim <jy0922.shim@samsung.com>
> > Cc: Kyungmin Park <kyungmin.park@samsung.com>
> > Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Cc: Mark Yao <mark.yao@rock-chips.com>
> > Cc: Seung-Woo Kim <sw0312.kim@samsung.com>
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> > drivers/gpu/drm/drm_fourcc.c | 43 +++++++++++++++++++++++++++++++++++++-
> > include/drm/drm_fourcc.h | 1 +-
> > 2 files changed, 44 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
> > index 9c0152df45ad..6e6227d6a46b 100644
> > --- a/drivers/gpu/drm/drm_fourcc.c
> > +++ b/drivers/gpu/drm/drm_fourcc.c
> > @@ -348,3 +348,46 @@ int drm_format_plane_height(int height, uint32_t
> > format, int plane) return height / info->vsub;
> > }
> > EXPORT_SYMBOL(drm_format_plane_height);
> > +
> > +/**
> > + * drm_format_has_alpha - get whether the format embeds an alpha component
> > + * @format: pixel format (DRM_FORMAT_*)
> > + *
> > + * Returns:
> > + * true if the format embeds an alpha component, false otherwise.
> > + */
> > +bool drm_format_has_alpha(uint32_t format)
> > +{
> > + switch (format) {
> > + case DRM_FORMAT_ARGB4444:
> > + case DRM_FORMAT_ABGR4444:
> > + case DRM_FORMAT_RGBA4444:
> > + case DRM_FORMAT_BGRA4444:
> > + case DRM_FORMAT_ARGB1555:
> > + case DRM_FORMAT_ABGR1555:
> > + case DRM_FORMAT_RGBA5551:
> > + case DRM_FORMAT_BGRA5551:
> > + case DRM_FORMAT_ARGB8888:
> > + case DRM_FORMAT_ABGR8888:
> > + case DRM_FORMAT_RGBA8888:
> > + case DRM_FORMAT_BGRA8888:
> > + case DRM_FORMAT_ARGB2101010:
> > + case DRM_FORMAT_ABGR2101010:
> > + case DRM_FORMAT_RGBA1010102:
> > + case DRM_FORMAT_BGRA1010102:
> > + case DRM_FORMAT_AYUV:
> > + case DRM_FORMAT_XRGB8888_A8:
> > + case DRM_FORMAT_XBGR8888_A8:
> > + case DRM_FORMAT_RGBX8888_A8:
> > + case DRM_FORMAT_BGRX8888_A8:
> > + case DRM_FORMAT_RGB888_A8:
> > + case DRM_FORMAT_BGR888_A8:
> > + case DRM_FORMAT_RGB565_A8:
> > + case DRM_FORMAT_BGR565_A8:
> > + return true;
> > +
> > + default:
> > + return false;
> > + }
> > +}
> > +EXPORT_SYMBOL(drm_format_has_alpha);
>
> How about adding the information to struct drm_format_info instead ?
> drm_format_has_alpha() could then be implemented as
>
> bool drm_format_has_alpha(uint32_t format)
> {
> const struct drm_format_info *info;
>
> info = drm_format_info(format);
> return info ? info->has_alpha : false;
> }
I considered it, and wasn't too sure about if adding more fields to
drm_format_info was ok. I can definitely do it that way.
> although drivers should really use the drm_framebuffer::format field directly
> in most cases, so the helper might not be needed at all.
The drivers converted in my serie shouldn't be too hard to convert to
use drm_format_info directly, so that can be removed as well.
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: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180109/4e77d4e3/attachment.sig>
^ permalink raw reply
* [PATCH v3] dt: psci: Update DT bindings to support hierarchical PSCI states
From: Ulf Hansson @ 2018-01-09 13:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <18b1d849-038d-f768-99aa-b363e4c8b2f8@arm.com>
On 9 January 2018 at 13:09, Sudeep Holla <sudeep.holla@arm.com> wrote:
> (Removed Brendan Jackman as he is no longer works in ARM)
>
> On 09/01/18 11:55, Ulf Hansson wrote:
>> From: Lina Iyer <lina.iyer@linaro.org>
>>
>> Update DT bindings to represent hierarchical CPU and CPU domain idle states
>> for PSCI. Also update the PSCI examples to clearly show how flattened and
>> hierarchical idle states can be represented in DT.
>>
>
> It now looks good to me :)
>
> Reviewed-by: Sudeep Holla <sudeep.holla@arm.com>
Rob, Sudeep, thanks for reviews!
Rob, do you want to pick this up now? Or how do you want me to manage
the patch going forward?
Kind regards
Uffe
^ permalink raw reply
* [PATCH 1/7] ARM: imx: add timer stop flag to ARM power off state
From: Stefan Agner @ 2018-01-09 13:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109092232.GA26312@b29396-OptiPlex-7040>
On 2018-01-09 10:22, Dong Aisheng wrote:
> On Tue, Jan 02, 2018 at 05:42:17PM +0100, Stefan Agner wrote:
>> When the CPU is in ARM power off state the ARM architected
>> timers are stopped. The flag is already present in the higher
>> power WAIT mode.
>>
>> This allows to use the ARM generic timer on i.MX 6UL/6ULL SoC.
>> Without the flag the kernel freezes when the timer enters the
>> first time ARM power off mode.
>>
>> Cc: Anson Huang <anson.huang@nxp.com>
>> Signed-off-by: Stefan Agner <stefan@agner.ch>
>
> It seems ok at my side.
> Did you meet the real issue? If yes, how to reproduce?
Enable the timer added with Patch 5, use a U-Boot with this patchset
applied:
https://www.mail-archive.com/u-boot at lists.denx.de/msg273287.html
And boot... For me it freezed somewhere early during systemd boot phase,
presumably the first time the CPU got into this idle mode.
--
Stefan
>
> Both mx6sx and mx6ul are using GPT which do not need that flag, suppose
> we should remove it, right?
> Anson can help confirm it.
>
> Regards
> Dong Aisheng
>
>> ---
>> arch/arm/mach-imx/cpuidle-imx6sx.c | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c
>> index c5a5c3a70ab1..d0f14b761ff7 100644
>> --- a/arch/arm/mach-imx/cpuidle-imx6sx.c
>> +++ b/arch/arm/mach-imx/cpuidle-imx6sx.c
>> @@ -89,6 +89,7 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
>> */
>> .exit_latency = 300,
>> .target_residency = 500,
>> + .flags = CPUIDLE_FLAG_TIMER_STOP,
>> .enter = imx6sx_enter_wait,
>> .name = "LOW-POWER-IDLE",
>> .desc = "ARM power off",
>> --
>> 2.15.1
>>
^ permalink raw reply
* [PATCH 5/7] ARM: dts: imx6ul: add ARM architected timer
From: Stefan Agner @ 2018-01-09 13:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109093422.GD26312@b29396-OptiPlex-7040>
On 2018-01-09 10:34, Dong Aisheng wrote:
> Hi Stefan,
>
> On Tue, Jan 02, 2018 at 05:42:21PM +0100, Stefan Agner wrote:
>> Add per-core ARM architected timer. Unfortunately bootloaders (U-Boot)
>> currently do not make the necessary initialization. Also specifing the
>> clock manually using the clock-frequency property seems not to help.
>> Therefor leave the timer disabled by default for now.
>>
>> Signed-off-by: Stefan Agner <stefan@agner.ch>
>
> Any special purpose to use arch timer?
>
It is the better option. It supports virtualization and allows direct
user space access, e.g. as used in OpenSSL through _armv7_tick.
--
Stefan
>> ---
>> arch/arm/boot/dts/imx6ul.dtsi | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi
>> index 993fbdbdd506..4d76923e8f44 100644
>> --- a/arch/arm/boot/dts/imx6ul.dtsi
>> +++ b/arch/arm/boot/dts/imx6ul.dtsi
>> @@ -110,6 +110,16 @@
>> <0x00a06000 0x2000>;
>> };
>>
>> + timer {
>> + compatible = "arm,armv7-timer";
>> + interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> + <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> + <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> + <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
>> + interrupt-parent = <&intc>;
>> + status = "disabled";
>> + };
>> +
>> ckil: clock-cli {
>> compatible = "fixed-clock";
>> #clock-cells = <0>;
>> --
>> 2.15.1
>>
^ permalink raw reply
* [net-next: PATCH 0/8] Armada 7k/8k PP2 ACPI support
From: Andrew Lunn @ 2018-01-09 13:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAPv3WKehwsrxuxemgL_5fKtUT2xjqkE6yf9DMcCcO2WRJ6nQaQ@mail.gmail.com>
On Tue, Jan 09, 2018 at 11:22:00AM +0100, Marcin Wojtas wrote:
> 2018-01-09 11:19 GMT+01:00 Graeme Gregory <graeme.gregory@linaro.org>:
> > On Mon, Jan 08, 2018 at 06:17:06PM +0100, Marcin Wojtas wrote:
> >> Hi Andrew,
> >>
> >>
> >>
> >> 2018-01-08 16:42 GMT+01:00 Andrew Lunn <andrew@lunn.ch>:
> >> > w> I am not familiar with MDIO, but if its similar or a specific
> >> >> implementation of a serial bus that does sound sane!
> >> >
> >>
> >> Thanks for digging, I will check if and how we can use
> >> GenericSerialBus with MDIO.
> >>
> > Maybe Lorenzo, Hanjun, Sudeep can comment here they might have come
> > across similar on other ARM boards.
> >
>
> I'm looking forward to their feedback, however, what I've noticed,
> each driver handles mdio/phys on its own, not using any generic
> solution, which is what I need to actually avoid :)
Agreed. Lets define it once for all drivers using phylib/phylink.
Andrew
^ permalink raw reply
* [PATCH 03/11] drm/bridge/synopsys: dw-hdmi: Enable workaround for v1.32a
From: Laurent Pinchart @ 2018-01-09 12:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171230210203.24115-4-jernej.skrabec@siol.net>
Hi Jernej,
Thank you for the patch.
On Saturday, 30 December 2017 23:01:55 EET Jernej Skrabec wrote:
> Allwinner SoCs have dw hdmi controller v1.32a which exhibits same
> magenta line issue as i.MX6Q and i.MX6DL. Enable workaround for it.
>
> Tests show that one iteration is enough.
>
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
This does not break R-Car DU, so
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> a38db40ce990..7ca14d7325b5 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -1634,9 +1634,10 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi
> *hdmi) * then write one of the FC registers several times.
> *
> * The number of iterations matters and depends on the HDMI TX revision
> - * (and possibly on the platform). So far only i.MX6Q (v1.30a) and
> - * i.MX6DL (v1.31a) have been identified as needing the workaround, with
> - * 4 and 1 iterations respectively.
> + * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
> + * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
> + * as needing the workaround, with 4 iterations for v1.30a and 1
> + * iteration for others.
> */
>
> switch (hdmi->version) {
> @@ -1644,6 +1645,7 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi
> *hdmi) count = 4;
> break;
> case 0x131a:
> + case 0x132a:
> count = 1;
> break;
> default:
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH v3 07/13] arm64: Add skeleton to harden the branch predictor against aliasing attacks
From: Philippe Ombredanne @ 2018-01-09 12:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515432758-26440-8-git-send-email-will.deacon@arm.com>
Dear Will,
On Mon, Jan 8, 2018 at 6:32 PM, Will Deacon <will.deacon@arm.com> wrote:
> Aliasing attacks against CPU branch predictors can allow an attacker to
> redirect speculative control flow on some CPUs and potentially divulge
> information from one context to another.
>
> This patch adds initial skeleton code behind a new Kconfig option to
> enable implementation-specific mitigations against these attacks for
> CPUs that are affected.
>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Will Deacon <will.deacon@arm.com>
<snip>
> --- /dev/null
> +++ b/arch/arm64/kernel/bpi.S
> @@ -0,0 +1,55 @@
> +/*
> + * Contains CPU specific branch predictor invalidation sequences
> + *
> + * Copyright (C) 2018 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
Could you consider using the new SDPX tags [1] instead of this long legalese?
Thanks!
[1] https://lkml.org/lkml/2017/12/28/323
--
Cordially
Philippe Ombredanne
^ permalink raw reply
* [linux-sunxi] [PATCH v2 13/16] power: supply: axp20x_battery: add support for AXP813
From: Julian Calaby @ 2018-01-09 12:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a1c061eb50edfd3519ece6c8177085b55d7aefc6.1515486346.git-series.quentin.schulz@free-electrons.com>
Hi Quentin,
On Tue, Jan 9, 2018 at 8:33 PM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> The X-Powers AXP813 PMIC has got some slight differences from
> AXP20X/AXP22X PMICs:
> - the maximum voltage supplied by the PMIC is 4.35 instead of 4.36/4.24
> for AXP20X/AXP22X,
> - the constant charge current formula is different,
>
> It also has a bit to tell whether the battery percentage returned by the
> PMIC is valid.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> drivers/power/supply/axp20x_battery.c | 42 ++++++++++++++++++++++++++++-
> 1 file changed, 42 insertions(+)
>
> diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
> index d73c78f..dad72a5 100644
> --- a/drivers/power/supply/axp20x_battery.c
> +++ b/drivers/power/supply/axp20x_battery.c
> @@ -46,6 +46,8 @@
> #define AXP20X_CHRG_CTRL1_TGT_4_2V (2 << 5)
> #define AXP20X_CHRG_CTRL1_TGT_4_36V (3 << 5)
>
> +#define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
> +
> #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
> #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
Should these be "alphabetical", i.e. AXP20X, AXP22X, AXP813?
Thanks,
--
Julian Calaby
Email: julian.calaby@gmail.com
Profile: http://www.google.com/profiles/julian.calaby/
^ permalink raw reply
* [RFC PATCH 2/2] drivers: clk: Add ZynqMP clock driver
From: Philippe Ombredanne @ 2018-01-09 12:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515449797-5629-3-git-send-email-jollys@xilinx.com>
Jolly,
On Mon, Jan 8, 2018 at 11:16 PM, Jolly Shah <jolly.shah@xilinx.com> wrote:
> This patch adds CCF compliant clock driver for ZynqMP.
> Clock driver queries supported clock information from
> firmware and regiters pll and output clocks with CCF.
>
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
> Signed-off-by: Tejas Patel <tejasp@xilinx.com>
> Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
> ---
<snip>
> .../devicetree/bindings/clock/zynq_mpsoc.txt | 163 +++++
> drivers/clk/Kconfig | 1 +
> drivers/clk/Makefile | 1 +
> drivers/clk/zynqmp/Kconfig | 8 +
> drivers/clk/zynqmp/Makefile | 3 +
> drivers/clk/zynqmp/clk-gate-zynqmp.c | 158 +++++
> drivers/clk/zynqmp/clk-mux-zynqmp.c | 190 ++++++
> drivers/clk/zynqmp/clkc.c | 707 +++++++++++++++++++++
> drivers/clk/zynqmp/divider.c | 239 +++++++
> drivers/clk/zynqmp/pll.c | 384 +++++++++++
> include/linux/clk/zynqmp.h | 46 ++
> 11 files changed, 1900 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
> create mode 100644 drivers/clk/zynqmp/Kconfig
> create mode 100644 drivers/clk/zynqmp/Makefile
> create mode 100644 drivers/clk/zynqmp/clk-gate-zynqmp.c
> create mode 100644 drivers/clk/zynqmp/clk-mux-zynqmp.c
> create mode 100644 drivers/clk/zynqmp/clkc.c
> create mode 100644 drivers/clk/zynqmp/divider.c
> create mode 100644 drivers/clk/zynqmp/pll.c
> create mode 100644 include/linux/clk/zynqmp.h
>
> diff --git a/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt b/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
> new file mode 100644
> index 0000000..9061b57
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
> @@ -0,0 +1,163 @@
> +Device Tree Clock bindings for the Zynq Ultrascale+ MPSoC
> +
> +The Zynq Ultrascale+ MPSoC has several different clk providers,
> +each with there own bindings.
> +The purpose of this document is to document their usage.
> +
> +See clock_bindings.txt for more information on the generic clock bindings.
> +
> +== Clock Controller ==
> +The clock controller is a logical abstraction of Zynq Ultrascale+ MPSoC clock
> +tree. It reads required input clock frequencies from the devicetree and acts
> +as clock provider for all clock consumers of PS clocks.
> +
> +Required properties:
> + - #clock-cells : Must be 1
> + - compatible : "xlnx,zynqmp-clkc"
> + - clocks : list of clock specifiers which are external input clocks to the
> + given clock controller. Please refer the next section to find
> + the input clocks for a given controller.
> + - clock-names : list of names of clocks which are exteral input clocks to the
> + given clock controller. Please refer to the clock bindings
> + for more details
> +
> +Input clocks for zynqmp Ultrascale+ clock controller:
> +The Zynq UltraScale+ MPSoC has one primary and four alternative reference clock
> +inputs.
> +These required clock inputs are the
> + - pss_ref_clk (PS reference clock)
> + - video_clk (reference clock for video system )
> + - pss_alt_ref_clk (alternative PS reference clock)
> + - aux_ref_clk
> + - gt_crx_ref_clk (transceiver reference clock)
> +
> +The following strings are optional parameters to the 'clock-names' property in
> +order to provide an optional (E)MIO clock source.
> + - swdt0_ext_clk
> + - swdt1_ext_clk
> + - gem0_emio_clk
> + - gem1_emio_clk
> + - gem2_emio_clk
> + - gem3_emio_clk
> + - mio_clk_XX # with XX = 00..77
> + - mio_clk_50_or_51 #for the mux clock to gem tsu from 50 or 51
> +
> +
> +Output clocks for zynqmp Ultrascale+ clock controller:
> +Output clocks are registered based on clock information received from firmware.
> +Output clock indexes are mentioned below:
> +
> +Clock ID: Output clock name:
> +-------------------------------------
> +0 iopll
> +1 rpll
> +2 apll
> +3 dpll
> +4 vpll
> +5 iopll_to_fpd
> +6 rpll_to_fpd
> +7 apll_to_lpd
> +8 dpll_to_lpd
> +9 vpll_to_lpd
> +10 acpu
> +11 acpu_half
> +12 dbf_fpd
> +13 dbf_lpd
> +14 dbg_trace
> +15 dbg_tstmp
> +16 dp_video_ref
> +17 dp_audio_ref
> +18 dp_stc_ref
> +19 gdma_ref
> +20 dpdma_ref
> +21 ddr_ref
> +22 sata_ref
> +23 pcie_ref
> +24 gpu_ref
> +25 gpu_pp0_ref
> +26 gpu_pp1_ref
> +27 topsw_main
> +28 topsw_lsbus
> +29 gtgref0_ref
> +30 lpd_switch
> +31 lpd_lsbus
> +32 usb0_bus_ref
> +33 usb1_bus_ref
> +34 usb3_dual_ref
> +35 usb0
> +36 usb1
> +37 cpu_r5
> +38 cpu_r5_core
> +39 csu_spb
> +40 csu_pll
> +41 pcap
> +42 iou_switch
> +43 gem_tsu_ref
> +44 gem_tsu
> +45 gem0_ref
> +46 gem1_ref
> +47 gem2_ref
> +48 gem3_ref
> +49 gem0_tx
> +50 gem1_tx
> +51 gem2_tx
> +52 gem3_tx
> +53 qspi_ref
> +54 sdio0_ref
> +55 sdio1_ref
> +56 uart0_ref
> +57 uart1_ref
> +58 spi0_ref
> +59 spi1_ref
> +60 nand_ref
> +61 i2c0_ref
> +62 i2c1_ref
> +63 can0_ref
> +64 can1_ref
> +65 can0
> +66 can1
> +67 dll_ref
> +68 adma_ref
> +69 timestamp_ref
> +70 ams_ref
> +71 pl0_ref
> +72 pl1_ref
> +73 pl2_ref
> +74 pl3_ref
> +75 wdt
> +76 iopll_int
> +77 iopll_pre_src
> +78 iopll_half
> +79 iopll_int_mux
> +80 iopll_post_src
> +81 rpll_int
> +82 rpll_pre_src
> +83 rpll_half
> +84 rpll_int_mux
> +85 rpll_post_src
> +86 apll_int
> +87 apll_pre_src
> +88 apll_half
> +89 apll_int_mux
> +90 apll_post_src
> +91 dpll_int
> +92 dpll_pre_src
> +93 dpll_half
> +94 dpll_int_mux
> +95 dpll_post_src
> +96 vpll_int
> +97 vpll_pre_src
> +98 vpll_half
> +99 vpll_int_mux
> +100 vpll_post_src
> +101 can0_mio
> +102 can1_mio
> +
> +Example:
> +
> +clkc: clkc at ff5e0020 {
> + #clock-cells = <1>;
> + compatible = "xlnx,zynqmp-clkc";
> + clocks = <&pss_ref_clk>, <&video_clk>, <&pss_alt_ref_clk>, <&aux_ref_clk>, <>_crx_ref_clk>;
> + clock-names = "pss_ref_clk", "video_clk", "pss_alt_ref_clk","aux_ref_clk", "gt_crx_ref_clk"
> +};
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 1c4e1aa..526f4f5 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -239,6 +239,7 @@ source "drivers/clk/samsung/Kconfig"
> source "drivers/clk/sunxi-ng/Kconfig"
> source "drivers/clk/tegra/Kconfig"
> source "drivers/clk/ti/Kconfig"
> +source "drivers/clk/zynqmp/Kconfig"
> source "drivers/clk/uniphier/Kconfig"
>
> endmenu
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f7f761b..d7328b4 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -98,3 +98,4 @@ obj-$(CONFIG_X86) += x86/
> endif
> obj-$(CONFIG_ARCH_ZX) += zte/
> obj-$(CONFIG_ARCH_ZYNQ) += zynq/
> +obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/
> diff --git a/drivers/clk/zynqmp/Kconfig b/drivers/clk/zynqmp/Kconfig
> new file mode 100644
> index 0000000..a6d54e9
> --- /dev/null
> +++ b/drivers/clk/zynqmp/Kconfig
> @@ -0,0 +1,8 @@
> +config COMMON_CLK_ZYNQMP
> + bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
> + depends on OF
> + depends on ARCH_ZYNQMP || COMPILE_TEST
> + help
> + Support for the Zynqmp Ultrascale clock controller.
> + It has a dependency on the PMU firmware.
> + Say Y if you want to support clock support
> diff --git a/drivers/clk/zynqmp/Makefile b/drivers/clk/zynqmp/Makefile
> new file mode 100644
> index 0000000..7d50f7a
> --- /dev/null
> +++ b/drivers/clk/zynqmp/Makefile
> @@ -0,0 +1,3 @@
> +# Zynq Ultrascale+ MPSoC clock specific Makefile
> +
> +obj-$(CONFIG_ARCH_ZYNQMP) += pll.o clk-gate-zynqmp.o divider.o clk-mux-zynqmp.o clkc.o
> diff --git a/drivers/clk/zynqmp/clk-gate-zynqmp.c b/drivers/clk/zynqmp/clk-gate-zynqmp.c
> new file mode 100644
> index 0000000..45eeed8
> --- /dev/null
> +++ b/drivers/clk/zynqmp/clk-gate-zynqmp.c
> @@ -0,0 +1,158 @@
> +/*
> + * Zynq UltraScale+ MPSoC clock controller
> + *
> + * Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier: GPL-2.0+
This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +/**
> + * struct clk_gate - gating clock
> + *
> + * @hw: handle between common and hardware-specific interfaces
> + * @flags: hardware-specific flags
> + * @clk_id: Id of clock
> + */
> +struct zynqmp_clk_gate {
> + struct clk_hw hw;
> + u8 flags;
> + u32 clk_id;
> +};
> +
> +#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw)
> +
> +/**
> + * zynqmp_clk_gate_enable - Enable clock
> + * @hw: handle between common and hardware-specific interfaces
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_clk_gate_enable(struct clk_hw *hw)
> +{
> + struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = gate->clk_id;
> + int ret = 0;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_enable)
> + return -ENXIO;
> +
> + ret = eemi_ops->clock_enable(clk_id);
> +
> + if (ret)
> + pr_warn_once("%s() clock enabled failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return 0;
> +}
> +
> +/*
> + * zynqmp_clk_gate_disable - Disable clock
> + * @hw: handle between common and hardware-specific interfaces
> + */
> +static void zynqmp_clk_gate_disable(struct clk_hw *hw)
> +{
> + struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = gate->clk_id;
> + int ret = 0;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_disable)
> + return;
> +
> + ret = eemi_ops->clock_disable(clk_id);
> +
> + if (ret)
> + pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +}
> +
> +/**
> + * zynqmp_clk_gate_is_enable - Check clock state
> + * @hw: handle between common and hardware-specific interfaces
> + *
> + * Return: 1 if enabled
> + * 0 if disabled
> + */
> +static int zynqmp_clk_gate_is_enabled(struct clk_hw *hw)
> +{
> + struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = gate->clk_id;
> + int state, ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_getstate)
> + return 0;
> +
> + ret = eemi_ops->clock_getstate(clk_id, &state);
> + if (ret)
> + pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return state ? 1 : 0;
> +}
> +
> +const struct clk_ops zynqmp_clk_gate_ops = {
> + .enable = zynqmp_clk_gate_enable,
> + .disable = zynqmp_clk_gate_disable,
> + .is_enabled = zynqmp_clk_gate_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(zynqmp_clk_gate_ops);
> +
> +/**
> + * zynqmp_clk_register_gate - register a gate clock with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of this clock
> + * @parents: name of this clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags for this clock
> + * @clk_gate_flags: gate-specific flags for this clock
> + *
> + * Return: clock handle of the registered clock gate
> + */
> +struct clk *zynqmp_clk_register_gate(struct device *dev, const char *name,
> + u32 clk_id, const char * const *parents,
> + u8 num_parents, unsigned long flags,
> + u8 clk_gate_flags)
> +{
> + struct zynqmp_clk_gate *gate;
> + struct clk *clk;
> + struct clk_init_data init;
> +
> + /* allocate the gate */
> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> + if (!gate)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = &zynqmp_clk_gate_ops;
> + init.flags = flags;
> + init.parent_names = parents;
> + init.num_parents = num_parents;
> +
> + /* struct clk_gate assignments */
> + gate->flags = clk_gate_flags;
> + gate->hw.init = &init;
> + gate->clk_id = clk_id;
> +
> + clk = clk_register(dev, &gate->hw);
> +
> + if (IS_ERR(clk))
> + kfree(gate);
> +
> + return clk;
> +}
> diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
> new file mode 100644
> index 0000000..ee36244
> --- /dev/null
> +++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
> @@ -0,0 +1,190 @@
> +/*
> + * Zynq UltraScale+ MPSoC mux
> + *
> + * Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier: GPL-2.0+
This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +
> +/*
> + * DOC: basic adjustable multiplexer clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is only affected by parent switching. No clk_set_rate support
> + * parent - parent is adjustable through clk_set_parent
> + */
> +
> +/**
> + * struct zynqmp_clk_mux - multiplexer clock
> + *
> + * @hw: handle between common and hardware-specific interfaces
> + * @flags: hardware-specific flags
> + * @clk_id: Id of clock
> + */
> +struct zynqmp_clk_mux {
> + struct clk_hw hw;
> + u8 flags;
> + u32 clk_id;
> +};
> +
> +#define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)
> +
> +/**
> + * zynqmp_clk_mux_get_parent - Get parent of clock
> + * @hw: handle between common and hardware-specific interfaces
> + *
> + * Return: Parent index
> + */
> +static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
> +{
> + struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = mux->clk_id;
> + u32 val;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_getparent)
> + return -ENXIO;
> +
> + ret = eemi_ops->clock_getparent(clk_id, &val);
> +
> + if (ret)
> + pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + if (val && (mux->flags & CLK_MUX_INDEX_BIT))
> + val = ffs(val) - 1;
> +
> + if (val && (mux->flags & CLK_MUX_INDEX_ONE))
> + val--;
> +
> + return val;
> +}
> +
> +/**
> + * zynqmp_clk_mux_set_parent - Set parent of clock
> + * @hw: handle between common and hardware-specific interfaces
> + * @index: Parent index
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = mux->clk_id;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_setparent)
> + return -ENXIO;
> +
> + if (mux->flags & CLK_MUX_INDEX_BIT)
> + index = 1 << index;
> +
> + if (mux->flags & CLK_MUX_INDEX_ONE)
> + index++;
> +
> + ret = eemi_ops->clock_setparent(clk_id, index);
> +
> + if (ret)
> + pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return 0;
> +}
> +
> +const struct clk_ops zynqmp_clk_mux_ops = {
> + .get_parent = zynqmp_clk_mux_get_parent,
> + .set_parent = zynqmp_clk_mux_set_parent,
> + .determine_rate = __clk_mux_determine_rate,
> +};
> +EXPORT_SYMBOL_GPL(zynqmp_clk_mux_ops);
> +
> +const struct clk_ops zynqmp_clk_mux_ro_ops = {
> + .get_parent = zynqmp_clk_mux_get_parent,
> +};
> +EXPORT_SYMBOL_GPL(zynqmp_clk_mux_ro_ops);
> +
> +/**
> + * zynqmp_clk_register_mux_table - register a mux table with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of this clock
> + * @parent_names: name of this clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags for this clock
> + * @clk_mux_flags: mux-specific flags for this clock
> + *
> + * Return: clock handle of the registered clock mux
> + */
> +struct clk *zynqmp_clk_register_mux_table(struct device *dev, const char *name,
> + u32 clk_id,
> + const char * const *parent_names,
> + u8 num_parents,
> + unsigned long flags,
> + u8 clk_mux_flags)
> +{
> + struct zynqmp_clk_mux *mux;
> + struct clk *clk;
> + struct clk_init_data init;
> +
> + /* allocate the mux */
> + mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> + if (!mux)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + if (clk_mux_flags & CLK_MUX_READ_ONLY)
> + init.ops = &zynqmp_clk_mux_ro_ops;
> + else
> + init.ops = &zynqmp_clk_mux_ops;
> + init.flags = flags;
> + init.parent_names = parent_names;
> + init.num_parents = num_parents;
> +
> + /* struct clk_mux assignments */
> + mux->flags = clk_mux_flags;
> + mux->hw.init = &init;
> + mux->clk_id = clk_id;
> +
> + clk = clk_register(dev, &mux->hw);
> +
> + if (IS_ERR(clk))
> + kfree(mux);
> +
> + return clk;
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux_table);
> +
> +/**
> + * zynqmp_clk_register_mux - register a mux clock with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of this clock
> + * @parent_names: name of this clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags for this clock
> + * @clk_mux_flags: mux-specific flags for this clock
> + *
> + * Return: clock handle of the registered clock mux
> + */
> +struct clk *zynqmp_clk_register_mux(struct device *dev, const char *name,
> + u32 clk_id, const char **parent_names,
> + u8 num_parents, unsigned long flags,
> + u8 clk_mux_flags)
> +{
> + return zynqmp_clk_register_mux_table(dev, name, clk_id, parent_names,
> + num_parents, flags, clk_mux_flags);
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);
> diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c
> new file mode 100644
> index 0000000..36bf1c1
> --- /dev/null
> +++ b/drivers/clk/zynqmp/clkc.c
> @@ -0,0 +1,707 @@
> +/*
> + * Zynq UltraScale+ MPSoC clock controller
> + *
> + * Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier: GPL-2.0+
This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+
> + *
> + * Based on drivers/clk/zynq/clkc.c
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#define MAX_PARENT 100
> +#define MAX_NODES 6
> +#define MAX_NAME_LEN 50
> +#define MAX_CLOCK 300
> +
> +#define CLK_INIT_ENABLE_SHIFT 1
> +#define CLK_TYPE_SHIFT 2
> +
> +#define PM_API_PAYLOAD_LEN 3
> +
> +#define NA_PARENT -1
> +#define DUMMY_PARENT -2
> +
> +#define CLK_TYPE_FIELD_LEN 4
> +#define CLK_TOPOLOGY_NODE_OFFSET 16
> +#define NODES_PER_RESP 3
> +
> +#define CLK_TYPE_FIELD_MASK 0xF
> +#define CLK_FLAG_FIELD_SHIFT 8
> +#define CLK_FLAG_FIELD_MASK 0x3FFF
> +#define CLK_TYPE_FLAG_FIELD_SHIFT 24
> +#define CLK_TYPE_FLAG_FIELD_MASK 0xFF
> +
> +#define CLK_PARENTS_ID_LEN 16
> +#define CLK_PARENTS_ID_MASK 0xFFFF
> +
> +/* Flags for parents */
> +#define PARENT_CLK_SELF 0
> +#define PARENT_CLK_NODE1 1
> +#define PARENT_CLK_NODE2 2
> +#define PARENT_CLK_NODE3 3
> +#define PARENT_CLK_NODE4 4
> +#define PARENT_CLK_EXTERNAL 5
> +
> +#define END_OF_CLK_NAME "END_OF_CLK"
> +#define RESERVED_CLK_NAME ""
> +
> +#define CLK_VALID_MASK 0x1
> +#define CLK_INIT_ENABLE_MASK (0x1 << CLK_INIT_ENABLE_SHIFT)
> +
> +enum clk_type {
> + CLK_TYPE_OUTPUT,
> + CLK_TYPE_EXTERNAL,
> +};
> +
> +/**
> + * struct clock_parent - Structure for parent of clock
> + * @id: Parent clock ID
> + * @flag: Parent flags
> + */
> +struct clock_parent {
> + char name[MAX_NAME_LEN];
> + int id;
> + u32 flag;
> +};
> +
> +/**
> + * struct clock_topology - Structure for topology of clock
> + * @type: Type of topology
> + * @flag: Topology flags
> + * @type_flag: Topology type specific flag
> + */
> +struct clock_topology {
> + u32 type;
> + u32 flag;
> + u32 type_flag;
> +};
> +
> +/**
> + * struct zynqmp_clock - Structure for clock
> + * @clk_name: Clock name
> + * @valid: Validity flag of clock
> + * @init_enable: init_enable flag of clock
> + * @topology: structure of topology of clock
> + * @num_node: Number of nodes present in topology
> + * @parent: structure of parent of clock
> + * @num_parents: Number of parents of clock
> + * @type: Type of clock
> + */
> +struct zynqmp_clock {
> + char clk_name[MAX_NAME_LEN];
> + u32 valid;
> + u32 init_enable;
> + enum clk_type type;
> + struct clock_topology node[MAX_NODES];
> + u32 num_nodes;
> + struct clock_parent parent[MAX_PARENT];
> + u32 num_parents;
> +};
> +
> +static const char clk_type_postfix[][10] = {
> + [TYPE_INVALID] = "",
> + [TYPE_MUX] = "_mux",
> + [TYPE_GATE] = "",
> + [TYPE_DIV1] = "_div1",
> + [TYPE_DIV2] = "_div2",
> + [TYPE_FIXEDFACTOR] = "_ff",
> + [TYPE_PLL] = ""
> +};
> +
> +static struct zynqmp_clock clock[MAX_CLOCK];
> +static struct clk_onecell_data zynqmp_clk_data;
> +static struct clk *zynqmp_clks[MAX_CLOCK];
> +static unsigned int clock_max_idx;
> +static const struct zynqmp_eemi_ops *eemi_ops;
> +
> +/**
> + * is_valid_clock - Check whether clock is valid or not
> + * @clk_id: Clock Index
> + * @valid: 1: if clock is valid
> + * 0: invalid clock
> + *
> + * Return: 0 Success
> + * Error code: Failure
> + */
> +static int is_valid_clock(u32 clk_id, u32 *valid)
> +{
> + if (clk_id < 0 || clk_id > clock_max_idx)
> + return -ENODEV;
> +
> + *valid = clock[clk_id].valid;
> +
> + return *valid ? 0 : -EINVAL;
> +}
> +
> +/**
> + * zynqmp_get_clock_name - Get name of clock from clock index
> + * @clk_id: Clock index
> + * @clk_name: Name of clock
> + *
> + * Return: 0: Success
> + * Error code: failure
> + */
> +static int zynqmp_get_clock_name(u32 clk_id, char *clk_name)
> +{
> + int ret;
> + u32 valid;
> +
> + ret = is_valid_clock(clk_id, &valid);
> + if (!ret && valid) {
> + strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN);
> + return 0;
> + } else {
> + return ret;
> + }
> +}
> +
> +/**
> + * get_clock_type - Get type of clock
> + * @clk_id: Clock Index
> + * @type: Clock type: CLK_TYPE_OUTPUT or CLK_TYPE_EXTERNAL
> + *
> + * Return: 0: Success
> + * Error code: failure
> + */
> +static int get_clock_type(u32 clk_id, u32 *type)
> +{
> + int ret;
> + u32 valid;
> +
> + ret = is_valid_clock(clk_id, &valid);
> + if (!ret && valid) {
> + *type = clock[clk_id].type;
> + return 0;
> + } else {
> + return ret;
> + }
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_name - Get the name of clock for given id
> + * @clock_id: ID of the clock to be queried
> + * @name: Name of given clock
> + *
> + * This function is used to get name of clock specified by given
> + * clock ID.
> + *
> + * Return: Returns status, in case of error name would be 0.
> + */
> +static int zynqmp_pm_clock_get_name(u32 clock_id, char *name)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> + qdata.qid = PM_QID_CLOCK_GET_NAME;
> + qdata.arg1 = clock_id;
> +
> + eemi_ops->query_data(qdata, ret_payload);
> + memcpy(name, ret_payload, CLK_GET_NAME_RESP_LEN);
> +
> + return 0;
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_topology - Get the topology of clock for given id
> + * @clock_id: ID of the clock to be queried
> + * @index: Node index of clock topology
> + * @topology: Buffer to store nodes in topology and flags
> + *
> + * This function is used to get topology information for the clock
> + * specified by given clock ID.
> + *
> + * This API will return 3 node of topology with a single response. To get
> + * other nodes, master should call same API in loop with new
> + * index till error is returned. E.g First call should have
> + * index 0 which will return nodes 0,1 and 2. Next call, index
> + * should be 3 which will return nodes 3,4 and 5 and so on.
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_topology(u32 clock_id, u32 index, u32 *topology)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> + qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY;
> + qdata.arg1 = clock_id;
> + qdata.arg2 = index;
> +
> + eemi_ops->query_data(qdata, ret_payload);
> + memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4);
> +
> + return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_fixedfactor_params - Get the clock's fixed factor
> + * parameters for fixed clock
> + * @clock_id: Clock ID
> + * @mul: Multiplication value
> + * @div: Divisor value
> + *
> + * This function is used to get fixed factor parameers for the fixed
> + * clock. This API is application only for the fixed clock.
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_fixedfactor_params(u32 clock_id,
> + u32 *mul,
> + u32 *div)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> + qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS;
> + qdata.arg1 = clock_id;
> +
> + eemi_ops->query_data(qdata, ret_payload);
> + *mul = ret_payload[1];
> + *div = ret_payload[2];
> +
> + return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_parents - Get the first 3 parents of clock for given id
> + * @clock_id: Clock ID
> + * @index: Parent index
> + * @parents: 3 parents of the given clock
> + *
> + * This function is used to get 3 parents for the clock specified by
> + * given clock ID.
> + *
> + * This API will return 3 parents with a single response. To get
> + * other parents, master should call same API in loop with new
> + * parent index till error is returned. E.g First call should have
> + * index 0 which will return parents 0,1 and 2. Next call, index
> + * should be 3 which will return parent 3,4 and 5 and so on.
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_parents(u32 clock_id, u32 index, u32 *parents)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> + qdata.qid = PM_QID_CLOCK_GET_PARENTS;
> + qdata.arg1 = clock_id;
> + qdata.arg2 = index;
> +
> + eemi_ops->query_data(qdata, ret_payload);
> + memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4);
> +
> + return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_attributes - Get the attributes of clock for given id
> + * @clock_id: Clock ID
> + * @attributes: Clock attributes
> + *
> + * This function is used to get clock's attributes(e.g. valid, clock type, etc).
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_attributes(u32 clock_id, u32 *attr)
> +{
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> + qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES;
> + qdata.arg1 = clock_id;
> +
> + eemi_ops->query_data(qdata, ret_payload);
> + memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4);
> +
> + return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * clock_get_topology: Get topology of clock from firmware using PM_API
> + * @clk_id: Clock Index
> + * @clk_topology: Structure of clock topology
> + * @num_nodes: number of nodes
> + *
> + * Return: 0: Success
> + * Error Code: Failure
> + */
> +static int clock_get_topology(u32 clk_id, struct clock_topology *clk_topology,
> + u32 *num_nodes)
> +{
> + int j, k = 0, ret;
> + u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
> +
> + *num_nodes = 0;
> + for (j = 0; j <= MAX_NODES; j += 3) {
> + ret = zynqmp_pm_clock_get_topology(clk_id, j, pm_resp);
> + if (ret)
> + return ret;
> + for (k = 0; k < PM_API_PAYLOAD_LEN; k++) {
> + if (!(pm_resp[k] & CLK_TYPE_FIELD_MASK))
> + goto done;
> + clk_topology[*num_nodes].type = pm_resp[k] &
> + CLK_TYPE_FIELD_MASK;
> + clk_topology[*num_nodes].flag =
> + (pm_resp[k] >> CLK_FLAG_FIELD_SHIFT) &
> + CLK_FLAG_FIELD_MASK;
> + clk_topology[*num_nodes].type_flag =
> + (pm_resp[k] >> CLK_TYPE_FLAG_FIELD_SHIFT) &
> + CLK_TYPE_FLAG_FIELD_MASK;
> + (*num_nodes)++;
> + }
> + }
> +done:
> + return 0;
> +}
> +
> +/**
> + * clock_get_parents: Get parents info from firmware using PM_API
> + * @clk_id: Clock Index
> + * @parent: Structure of parent information
> + * @num_parents: Total number of parents
> + *
> + * Return: 0: Success
> + * Error code: Failure
> + */
> +static int clock_get_parents(u32 clk_id, struct clock_parent *parents,
> + u32 *num_parents)
> +{
> + int j = 0, k, ret, total_parents = 0;
> + u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
> +
> + do {
> + /* Get parents from firmware */
> + ret = zynqmp_pm_clock_get_parents(clk_id, j, pm_resp);
> + if (ret)
> + return ret;
> +
> + for (k = 0; k < PM_API_PAYLOAD_LEN; k++) {
> + if (pm_resp[k] == (u32)NA_PARENT) {
> + *num_parents = total_parents;
> + return 0;
> + }
> +
> + parents[k + j].id = pm_resp[k] & CLK_PARENTS_ID_MASK;
> + if (parents[k + j].id == DUMMY_PARENT) {
> + strncpy(parents[k + j].name,
> + "dummy_name", MAX_NAME_LEN);
> + parents[k + j].flag = 0;
> + } else {
> + parents[k + j].flag = pm_resp[k] >>
> + CLK_PARENTS_ID_LEN;
> + if (zynqmp_get_clock_name(parents[k + j].id,
> + parents[k + j].name))
> + continue;
> + }
> + total_parents++;
> + }
> + j += PM_API_PAYLOAD_LEN;
> + } while (total_parents <= MAX_PARENT);
> + return 0;
> +}
> +
> +/**
> + * get_parent_list: Create list of parents name
> + * @np: Device node
> + * @clk_id: Clock Index
> + * @parent_list List of parent's name
> + * @num_parents: Total number of parents
> + *
> + # Return: 0: Success
> + * Error code: Failure
> + */
> +static int get_parent_list(struct device_node *np, u32 clk_id,
> + const char **parent_list, u32 *num_parents)
> +{
> + int i = 0, ret;
> + u32 total_parents = clock[clk_id].num_parents;
> + struct clock_topology *clk_nodes;
> + struct clock_parent *parents;
> +
> + clk_nodes = clock[clk_id].node;
> + parents = clock[clk_id].parent;
> +
> + for (i = 0; i < total_parents; i++) {
> + if (!parents[i].flag) {
> + parent_list[i] = parents[i].name;
> + } else if (parents[i].flag == PARENT_CLK_EXTERNAL) {
> + ret = of_property_match_string(np, "clock-names",
> + parents[i].name);
> + if (ret < 0)
> + strncpy(parents[i].name,
> + "dummy_name", MAX_NAME_LEN);
> + parent_list[i] = parents[i].name;
> + } else {
> + strcat(parents[i].name,
> + clk_type_postfix[clk_nodes[parents[i].flag - 1].
> + type]);
> + parent_list[i] = parents[i].name;
> + }
> + }
> +
> + *num_parents = total_parents;
> + return 0;
> +}
> +
> +/**
> + * zynqmp_register_clk_topology: Register clock topology
> + * @clk_id: Clock Index
> + * @clk_name: Clock Name
> + * @num_parents: Total number of parents
> + * @parent_names: List of parents name
> + *
> + * Return: 0: Success
> + * Error code: Failure
> + */
> +static struct clk *zynqmp_register_clk_topology(int clk_id, char *clk_name,
> + int num_parents,
> + const char **parent_names)
> +{
> + int j, ret;
> + u32 num_nodes, mult, div;
> + char *clk_out = NULL;
> + struct clock_topology *nodes;
> + struct clk *clk = NULL;
> +
> + nodes = clock[clk_id].node;
> + num_nodes = clock[clk_id].num_nodes;
> +
> + for (j = 0; j < num_nodes; j++) {
> + if (j != (num_nodes - 1)) {
> + clk_out = kasprintf(GFP_KERNEL, "%s%s", clk_name,
> + clk_type_postfix[nodes[j].type]);
> + } else {
> + clk_out = kasprintf(GFP_KERNEL, "%s", clk_name);
> + }
> +
> + switch (nodes[j].type) {
> + case TYPE_MUX:
> + clk = zynqmp_clk_register_mux(NULL, clk_out,
> + clk_id, parent_names,
> + num_parents,
> + nodes[j].flag,
> + nodes[j].type_flag);
> + break;
> + case TYPE_PLL:
> + clk = clk_register_zynqmp_pll(clk_out, clk_id,
> + parent_names, 1,
> + nodes[j].flag);
> + break;
> + case TYPE_FIXEDFACTOR:
> + ret = zynqmp_pm_clock_get_fixedfactor_params(clk_id,
> + &mult,
> + &div);
> + clk = clk_register_fixed_factor(NULL, clk_out,
> + parent_names[0],
> + nodes[j].flag, mult,
> + div);
> + break;
> + case TYPE_DIV1:
> + case TYPE_DIV2:
> + clk = zynqmp_clk_register_divider(NULL, clk_out, clk_id,
> + nodes[j].type,
> + parent_names, 1,
> + nodes[j].flag,
> + nodes[j].type_flag);
> + break;
> + case TYPE_GATE:
> + clk = zynqmp_clk_register_gate(NULL, clk_out, clk_id,
> + parent_names, 1,
> + nodes[j].flag,
> + nodes[j].type_flag);
> + break;
> + default:
> + pr_err("%s() Unknown topology for %s\n",
> + __func__, clk_out);
> + break;
> + }
> + if (IS_ERR(clk))
> + pr_warn_once("%s() %s register fail with %ld\n",
> + __func__, clk_name, PTR_ERR(clk));
> +
> + parent_names[0] = clk_out;
> + }
> + kfree(clk_out);
> + return clk;
> +}
> +
> +/**
> + * zynqmp_register_clocks: Register clocks
> + * @np: Device node
> + *
> + * Return: 0: Success
> + * Error code: failure
> + */
> +static int zynqmp_register_clocks(struct device_node *np)
> +{
> + int ret;
> + u32 i, total_parents = 0, type = 0;
> + const char *parent_names[MAX_PARENT];
> +
> + for (i = 0; i < clock_max_idx; i++) {
> + char clk_name[MAX_NAME_LEN];
> +
> + /* get clock name, continue to next clock if name not found */
> + if (zynqmp_get_clock_name(i, clk_name))
> + continue;
> +
> + /* Check if clock is valid and output clock.
> + * Do not regiter invalid or external clock.
> + */
> + ret = get_clock_type(i, &type);
> + if (ret || type != CLK_TYPE_OUTPUT)
> + continue;
> +
> + /* Get parents of clock*/
> + if (get_parent_list(np, i, parent_names, &total_parents)) {
> + WARN_ONCE(1, "No parents found for %s\n",
> + clock[i].clk_name);
> + continue;
> + }
> +
> + zynqmp_clks[i] = zynqmp_register_clk_topology(i, clk_name,
> + total_parents,
> + parent_names);
> +
> + /* Enable clock if init_enable flag is 1 */
> + if (clock[i].init_enable)
> + clk_prepare_enable(zynqmp_clks[i]);
> + }
> +
> + for (i = 0; i < clock_max_idx; i++) {
> + if (IS_ERR(zynqmp_clks[i])) {
> + pr_err("Zynq Ultrascale+ MPSoC clk %s: register failed with %ld\n",
> + clock[i].clk_name, PTR_ERR(zynqmp_clks[i]));
> + WARN_ON(1);
> + }
> + }
> + return 0;
> +}
> +
> +/**
> + * zynqmp_get_clock_info - Get clock information from firmware using PM_API
> + */
> +static void zynqmp_get_clock_info(void)
> +{
> + int i, ret;
> + u32 attr, type = 0;
> +
> + memset(clock, 0, sizeof(clock));
> + for (i = 0; i < MAX_CLOCK; i++) {
> + zynqmp_pm_clock_get_name(i, clock[i].clk_name);
> + if (!strncmp(clock[i].clk_name, END_OF_CLK_NAME,
> + MAX_NAME_LEN)) {
> + clock_max_idx = i;
> + break;
> + } else if (!strncmp(clock[i].clk_name, RESERVED_CLK_NAME,
> + MAX_NAME_LEN)) {
> + continue;
> + }
> +
> + ret = zynqmp_pm_clock_get_attributes(i, &attr);
> + if (ret)
> + continue;
> +
> + clock[i].valid = attr & CLK_VALID_MASK;
> + clock[i].init_enable = !!(attr & CLK_INIT_ENABLE_MASK);
> + clock[i].type = attr >> CLK_TYPE_SHIFT ? CLK_TYPE_EXTERNAL :
> + CLK_TYPE_OUTPUT;
> + }
> +
> + /* Get topology of all clock */
> + for (i = 0; i < clock_max_idx; i++) {
> + ret = get_clock_type(i, &type);
> + if (ret || type != CLK_TYPE_OUTPUT)
> + continue;
> +
> + ret = clock_get_topology(i, clock[i].node, &clock[i].num_nodes);
> + if (ret)
> + continue;
> +
> + ret = clock_get_parents(i, clock[i].parent,
> + &clock[i].num_parents);
> + if (ret)
> + continue;
> + }
> +}
> +
> +/**
> + * zynqmp_clk_setup - Setup the clock framework and register clocks
> + * @np: Device node
> + */
> +static void __init zynqmp_clk_setup(struct device_node *np)
> +{
> + int idx;
> +
> + idx = of_property_match_string(np, "clock-names", "pss_ref_clk");
> + if (idx < 0) {
> + pr_err("pss_ref_clk not provided\n");
> + return;
> + }
> + idx = of_property_match_string(np, "clock-names", "video_clk");
> + if (idx < 0) {
> + pr_err("video_clk not provided\n");
> + return;
> + }
> + idx = of_property_match_string(np, "clock-names", "pss_alt_ref_clk");
> + if (idx < 0) {
> + pr_err("pss_alt_ref_clk not provided\n");
> + return;
> + }
> + idx = of_property_match_string(np, "clock-names", "aux_ref_clk");
> + if (idx < 0) {
> + pr_err("aux_ref_clk not provided\n");
> + return;
> + }
> + idx = of_property_match_string(np, "clock-names", "gt_crx_ref_clk");
> + if (idx < 0) {
> + pr_err("aux_ref_clk not provided\n");
> + return;
> + }
> +
> + zynqmp_get_clock_info();
> + zynqmp_register_clocks(np);
> +
> + zynqmp_clk_data.clks = zynqmp_clks;
> + zynqmp_clk_data.clk_num = clock_max_idx;
> + of_clk_add_provider(np, of_clk_src_onecell_get, &zynqmp_clk_data);
> +}
> +
> +/**
> + * zynqmp_clock_init - Initialize zynqmp clocks
> + *
> + * Return: 0 always
> + */
> +static int __init zynqmp_clock_init(void)
> +{
> + struct device_node *np;
> +
> + np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-clkc");
> + if (!np) {
> + pr_err("%s: clkc node not found\n", __func__);
> + of_node_put(np);
> + return 0;
> + }
> +
> + eemi_ops = get_eemi_ops();
> + if (!eemi_ops || !eemi_ops->query_data) {
> + pr_err("%s: clk data not found\n", __func__);
> + of_node_put(np);
> + return 0;
> + }
> +
> + zynqmp_clk_setup(np);
> +
> + return 0;
> +}
> +arch_initcall(zynqmp_clock_init);
> diff --git a/drivers/clk/zynqmp/divider.c b/drivers/clk/zynqmp/divider.c
> new file mode 100644
> index 0000000..1a1473c
> --- /dev/null
> +++ b/drivers/clk/zynqmp/divider.c
> @@ -0,0 +1,239 @@
> +/*
> + * Zynq UltraScale+ MPSoC Divider support
> + *
> + * Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier: GPL-2.0+
Same as above: This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+
> + *
> + * Adjustable divider clock implementation
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +#include <linux/log2.h>
> +
> +/*
> + * DOC: basic adjustable divider clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is adjustable. clk->rate = ceiling(parent->rate / divisor)
> + * parent - fixed parent. No clk_set_parent support
> + */
> +
> +#define to_zynqmp_clk_divider(_hw) \
> + container_of(_hw, struct zynqmp_clk_divider, hw)
> +
> +/**
> + * struct zynqmp_clk_divider - adjustable divider clock
> + *
> + * @hw: handle between common and hardware-specific interfaces
> + * @flags: Hardware specific flags
> + * @clk_id: Id of clock
> + * @div_type: divisor type (TYPE_DIV1 or TYPE_DIV2)
> + */
> +struct zynqmp_clk_divider {
> + struct clk_hw hw;
> + u8 flags;
> + u32 clk_id;
> + u32 div_type;
> +};
> +
> +static int zynqmp_divider_get_val(unsigned long parent_rate, unsigned long rate)
> +{
> + return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
> +}
> +
> +static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = divider->clk_id;
> + u32 div_type = divider->div_type;
> + u32 div, value;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_getdivider)
> + return -ENXIO;
> +
> + ret = eemi_ops->clock_getdivider(clk_id, &div);
> +
> + if (ret)
> + pr_warn_once("%s() get divider failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + if (div_type == TYPE_DIV1)
> + value = div & 0xFFFF;
> + else
> + value = (div >> 16) & 0xFFFF;
> +
> + return zynqmp_divider_get_val((u64)parent_rate, value);
> +}
> +
> +static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *prate)
> +{
> + struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = divider->clk_id;
> + u32 div_type = divider->div_type;
> + u32 bestdiv;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_getdivider)
> + return -ENXIO;
> +
> + /* if read only, just return current value */
> + if (divider->flags & CLK_DIVIDER_READ_ONLY) {
> + ret = eemi_ops->clock_getdivider(clk_id, &bestdiv);
> +
> + if (ret)
> + pr_warn_once("%s() get divider failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> + if (div_type == TYPE_DIV1)
> + bestdiv = bestdiv & 0xFFFF;
> + else
> + bestdiv = (bestdiv >> 16) & 0xFFFF;
> +
> + return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
> + }
> +
> + bestdiv = zynqmp_divider_get_val(*prate, rate);
> +
> + if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) &&
> + ((clk_hw_get_flags(hw) & CLK_FRAC)))
> + bestdiv = rate % *prate ? 1 : bestdiv;
> + *prate = rate * bestdiv;
> +
> + return rate;
> +}
> +
> +/**
> + * zynqmp_clk_divider_set_rate - Set rate of divider clock
> + * @hw: handle between common and hardware-specific interfaces
> + * @rate: rate of clock to be set
> + * @parent_rate: rate of parent clock
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = divider->clk_id;
> + u32 div_type = divider->div_type;
> + u32 value, div;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_setdivider)
> + return -ENXIO;
> +
> + value = zynqmp_divider_get_val(parent_rate, rate);
> + if (div_type == TYPE_DIV1) {
> + div = value & 0xFFFF;
> + div |= ((u16)-1) << 16;
> + } else {
> + div = ((u16)-1);
> + div |= value << 16;
> + }
> +
> + ret = eemi_ops->clock_setdivider(clk_id, div);
> +
> + if (ret)
> + pr_warn_once("%s() set divider failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return 0;
> +}
> +
> +static const struct clk_ops zynqmp_clk_divider_ops = {
> + .recalc_rate = zynqmp_clk_divider_recalc_rate,
> + .round_rate = zynqmp_clk_divider_round_rate,
> + .set_rate = zynqmp_clk_divider_set_rate,
> +};
> +
> +/**
> + * _register_divider - register a divider clock
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of clock
> + * @div_type: Type of divisor
> + * @parents: name of clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags
> + * @clk_divider_flags: divider-specific flags for this clock
> + *
> + * Return: handle to registered clock divider
> + */
> +static struct clk *_register_divider(struct device *dev, const char *name,
> + u32 clk_id, u32 div_type,
> + const char * const *parents,
> + u8 num_parents, unsigned long flags,
> + u8 clk_divider_flags)
> +{
> + struct zynqmp_clk_divider *div;
> + struct clk *clk;
> + struct clk_init_data init;
> +
> + /* allocate the divider */
> + div = kzalloc(sizeof(*div), GFP_KERNEL);
> + if (!div)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = &zynqmp_clk_divider_ops;
> + init.flags = flags;
> + init.parent_names = parents;
> + init.num_parents = num_parents;
> +
> + /* struct clk_divider assignments */
> + div->flags = clk_divider_flags;
> + div->hw.init = &init;
> + div->clk_id = clk_id;
> + div->div_type = div_type;
> +
> + /* register the clock */
> + clk = clk_register(dev, &div->hw);
> +
> + if (IS_ERR(clk))
> + kfree(div);
> +
> + return clk;
> +}
> +
> +/**
> + * zynqmp_clk_register_divider - register a divider clock
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of clock
> + * @div_type: Type of divisor
> + * @parents: name of clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags
> + * @clk_divider_flags: divider-specific flags for this clock
> + *
> + * Return: handle to registered clock divider
> + */
> +struct clk *zynqmp_clk_register_divider(struct device *dev, const char *name,
> + u32 clk_id, u32 div_type,
> + const char * const *parents,
> + u8 num_parents, unsigned long flags,
> + u8 clk_divider_flags)
> +{
> + return _register_divider(dev, name, clk_id, div_type, parents,
> + num_parents, flags, clk_divider_flags);
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_clk_register_divider);
> diff --git a/drivers/clk/zynqmp/pll.c b/drivers/clk/zynqmp/pll.c
> new file mode 100644
> index 0000000..75def21
> --- /dev/null
> +++ b/drivers/clk/zynqmp/pll.c
> @@ -0,0 +1,384 @@
> +/*
> + * Zynq UltraScale+ MPSoC PLL driver
> + *
> + * Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier: GPL-2.0+
Same as above: This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+
> + */
> +#include <linux/clk.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +
> +/**
> + * struct zynqmp_pll - Structure for PLL clock
> + * @hw: Handle between common and hardware-specific interfaces
> + * @clk_id: PLL clock ID
> + */
> +struct zynqmp_pll {
> + struct clk_hw hw;
> + u32 clk_id;
> +};
> +
> +#define to_zynqmp_pll(_hw) container_of(_hw, struct zynqmp_pll, hw)
> +
> +/* Register bitfield defines */
> +#define PLLCTRL_FBDIV_MASK 0x7f00
> +#define PLLCTRL_FBDIV_SHIFT 8
> +#define PLLCTRL_BP_MASK BIT(3)
> +#define PLLCTRL_DIV2_MASK BIT(16)
> +#define PLLCTRL_RESET_MASK 1
> +#define PLLCTRL_RESET_VAL 1
> +#define PLL_STATUS_LOCKED 1
> +#define PLLCTRL_RESET_SHIFT 0
> +#define PLLCTRL_DIV2_SHIFT 16
> +
> +#define PLL_FBDIV_MIN 25
> +#define PLL_FBDIV_MAX 125
> +
> +#define PS_PLL_VCO_MIN 1500000000
> +#define PS_PLL_VCO_MAX 3000000000UL
> +
> +enum pll_mode {
> + PLL_MODE_INT,
> + PLL_MODE_FRAC,
> +};
> +
> +#define FRAC_OFFSET 0x8
> +#define PLLFCFG_FRAC_EN BIT(31)
> +#define FRAC_DIV 0x10000 /* 2^16 */
> +
> +/**
> + * pll_get_mode - Get mode of PLL
> + * @hw: Handle between common and hardware-specific interfaces
> + *
> + * Return: Mode of PLL
> + */
> +static inline enum pll_mode pll_get_mode(struct clk_hw *hw)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + u32 clk_id = clk->clk_id;
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->ioctl)
> + return -ENXIO;
> +
> + ret = eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_MODE, clk_id, 0,
> + ret_payload);
> + if (ret)
> + pr_warn_once("%s() PLL get frac mode failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return ret_payload[1];
> +}
> +
> +/**
> + * pll_set_mode - Set the PLL mode
> + * @hw: Handle between common and hardware-specific interfaces
> + * @on: Flag to determine the mode
> + */
> +static inline void pll_set_mode(struct clk_hw *hw, bool on)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + u32 clk_id = clk->clk_id;
> + const char *clk_name = clk_hw_get_name(hw);
> + int ret;
> + u32 mode;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->ioctl) {
> + pr_warn_once("eemi_ops not found\n");
> + return;
> + }
> +
> + if (on)
> + mode = PLL_MODE_FRAC;
> + else
> + mode = PLL_MODE_INT;
> +
> + ret = eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode, NULL);
> + if (ret)
> + pr_warn_once("%s() PLL set frac mode failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +}
> +
> +/**
> + * zynqmp_pll_round_rate - Round a clock frequency
> + * @hw: Handle between common and hardware-specific interfaces
> + * @rate: Desired clock frequency
> + * @prate: Clock frequency of parent clock
> + *
> + * Return: Frequency closest to @rate the hardware can generate
> + */
> +static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + u32 fbdiv;
> + long rate_div, f;
> +
> + /* Enable the fractional mode if needed */
> + rate_div = ((rate * FRAC_DIV) / *prate);
> + f = rate_div % FRAC_DIV;
> + pll_set_mode(hw, !!f);
> +
> + if (pll_get_mode(hw) == PLL_MODE_FRAC) {
> + if (rate > PS_PLL_VCO_MAX) {
> + fbdiv = rate / PS_PLL_VCO_MAX;
> + rate = rate / (fbdiv + 1);
> + }
> + if (rate < PS_PLL_VCO_MIN) {
> + fbdiv = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate);
> + rate = rate * fbdiv;
> + }
> + return rate;
> + }
> +
> + fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
> + fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
> + return *prate * fbdiv;
> +}
> +
> +/**
> + * zynqmp_pll_recalc_rate - Recalculate clock frequency
> + * @hw: Handle between common and hardware-specific interfaces
> + * @parent_rate: Clock frequency of parent clock
> + * Return: Current clock frequency
> + */
> +static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + u32 clk_id = clk->clk_id;
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 fbdiv, data;
> + unsigned long rate, frac;
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_getdivider)
> + return 0;
> +
> + /*
> + * makes probably sense to redundantly save fbdiv in the struct
> + * zynqmp_pll to save the IO access.
> + */
> + ret = eemi_ops->clock_getdivider(clk_id, &fbdiv);
> + if (ret)
> + pr_warn_once("%s() get divider failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + rate = parent_rate * fbdiv;
> + if (pll_get_mode(hw) == PLL_MODE_FRAC) {
> + eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_DATA, clk_id, 0,
> + ret_payload);
> + data = ret_payload[1];
> + frac = (parent_rate * data) / FRAC_DIV;
> + rate = rate + frac;
> + }
> +
> + return rate;
> +}
> +
> +/**
> + * zynqmp_pll_set_rate - Set rate of PLL
> + * @hw: Handle between common and hardware-specific interfaces
> + * @rate: Frequency of clock to be set
> + * @parent_rate: Clock frequency of parent clock
> + */
> +static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + u32 clk_id = clk->clk_id;
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 fbdiv, data;
> + long rate_div, frac, m, f;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_setdivider)
> + return -ENXIO;
> +
> + if (pll_get_mode(hw) == PLL_MODE_FRAC) {
> + unsigned int children;
> +
> + /*
> + * We're running on a ZynqMP compatible machine, make sure the
> + * VPLL only has one child.
> + */
> + children = clk_get_children("vpll");
> +
> + /* Account for vpll_to_lpd and dp_video_ref */
> + if (children > 2)
> + WARN(1, "Two devices are using vpll which is forbidden\n");
> +
> + rate_div = ((rate * FRAC_DIV) / parent_rate);
> + m = rate_div / FRAC_DIV;
> + f = rate_div % FRAC_DIV;
> + m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
> + rate = parent_rate * m;
> + frac = (parent_rate * f) / FRAC_DIV;
> +
> + ret = eemi_ops->clock_setdivider(clk_id, m);
> + if (ret)
> + pr_warn_once("%s() set divider failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + data = (FRAC_DIV * f) / FRAC_DIV;
> + eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data, NULL);
> +
> + return (rate + frac);
> + }
> +
> + fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
> + fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
> + ret = eemi_ops->clock_setdivider(clk_id, fbdiv);
> + if (ret)
> + pr_warn_once("%s() set divider failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return parent_rate * fbdiv;
> +}
> +
> +/**
> + * zynqmp_pll_is_enabled - Check if a clock is enabled
> + * @hw: Handle between common and hardware-specific interfaces
> + *
> + * Return: 1 if the clock is enabled, 0 otherwise
> + */
> +static int zynqmp_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = clk->clk_id;
> + unsigned int state;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_getstate)
> + return 0;
> +
> + ret = eemi_ops->clock_getstate(clk_id, &state);
> + if (ret)
> + pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return state ? 1 : 0;
> +}
> +
> +/**
> + * zynqmp_pll_enable - Enable clock
> + * @hw: Handle between common and hardware-specific interfaces
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_pll_enable(struct clk_hw *hw)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = clk->clk_id;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_enable)
> + return 0;
> +
> + if (zynqmp_pll_is_enabled(hw))
> + return 0;
> +
> + pr_info("PLL: enable\n");
> +
> + ret = eemi_ops->clock_enable(clk_id);
> + if (ret)
> + pr_warn_once("%s() clock enable failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +
> + return 0;
> +}
> +
> +/**
> + * zynqmp_pll_disable - Disable clock
> + * @hw: Handle between common and hardware-specific interfaces
> + *
> + */
> +static void zynqmp_pll_disable(struct clk_hw *hw)
> +{
> + struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> + const char *clk_name = clk_hw_get_name(hw);
> + u32 clk_id = clk->clk_id;
> + int ret;
> + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> + if (!eemi_ops || !eemi_ops->clock_disable)
> + return;
> +
> + if (!zynqmp_pll_is_enabled(hw))
> + return;
> +
> + pr_info("PLL: shutdown\n");
> +
> + ret = eemi_ops->clock_disable(clk_id);
> + if (ret)
> + pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
> + __func__, clk_name, ret);
> +}
> +
> +static const struct clk_ops zynqmp_pll_ops = {
> + .enable = zynqmp_pll_enable,
> + .disable = zynqmp_pll_disable,
> + .is_enabled = zynqmp_pll_is_enabled,
> + .round_rate = zynqmp_pll_round_rate,
> + .recalc_rate = zynqmp_pll_recalc_rate,
> + .set_rate = zynqmp_pll_set_rate,
> +};
> +
> +/**
> + * clk_register_zynqmp_pll - Register PLL with the clock framework
> + * @name: PLL name
> + * @flag: PLL flags
> + * @parents: Parent clock names
> + * @num_parents:Number of parents
> + * @pll_ctrl: Pointer to PLL control register
> + * @pll_status: Pointer to PLL status register
> + * @lock_index: Bit index to this PLL's lock status bit in @pll_status
> + *
> + * Return: Handle to the registered clock
> + */
> +struct clk *clk_register_zynqmp_pll(const char *name, u32 clk_id,
> + const char * const *parents,
> + u8 num_parents, unsigned long flag)
> +{
> + struct zynqmp_pll *pll;
> + struct clk *clk;
> + struct clk_init_data init;
> + int status;
> +
> + init.name = name;
> + init.ops = &zynqmp_pll_ops;
> + init.flags = flag;
> + init.parent_names = parents;
> + init.num_parents = num_parents;
> +
> + pll = kmalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + /* Populate the struct */
> + pll->hw.init = &init;
> + pll->clk_id = clk_id;
> +
> + clk = clk_register(NULL, &pll->hw);
> + if (WARN_ON(IS_ERR(clk)))
> + kfree(pll);
> +
> + status = clk_set_rate_range(clk, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
> + if (status < 0)
> + pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, status);
> +
> + return clk;
> +}
> diff --git a/include/linux/clk/zynqmp.h b/include/linux/clk/zynqmp.h
> new file mode 100644
> index 0000000..024ebf8
> --- /dev/null
> +++ b/include/linux/clk/zynqmp.h
> @@ -0,0 +1,46 @@
> +/*
> + * Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
This tag should be on the fist line this way:
/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#ifndef __LINUX_CLK_ZYNQMP_H_
> +#define __LINUX_CLK_ZYNQMP_H_
> +
> +#include <linux/spinlock.h>
> +#include <linux/firmware/xilinx/zynqmp/firmware.h>
> +
> +#define CLK_FRAC BIT(13) /* has a fractional parent */
> +
> +struct device;
> +
> +struct clk *clk_register_zynqmp_pll(const char *name, u32 clk_id,
> + const char * const *parent, u8 num_parents,
> + unsigned long flag);
> +
> +struct clk *zynqmp_clk_register_gate(struct device *dev, const char *name,
> + u32 clk_id,
> + const char * const *parent_name,
> + u8 num_parents, unsigned long flags,
> + u8 clk_gate_flags);
> +
> +struct clk *zynqmp_clk_register_divider(struct device *dev, const char *name,
> + u32 clk_id, u32 div_type,
> + const char * const *parent_name,
> + u8 num_parents,
> + unsigned long flags,
> + u8 clk_divider_flags);
> +
> +struct clk *zynqmp_clk_register_mux(struct device *dev, const char *name,
> + u32 clk_id,
> + const char **parent_names,
> + u8 num_parents, unsigned long flags,
> + u8 clk_mux_flags);
> +
> +struct clk *zynqmp_clk_register_mux_table(struct device *dev, const char *name,
> + u32 clk_id,
> + const char * const *parent_names,
> + u8 num_parents, unsigned long flags,
> + u8 clk_mux_flags);
> +
> +#endif
> --
> 2.7.4
>
--
Cordially
Philippe Ombredanne
^ permalink raw reply
* [PATCH] ARM: i.MX6: add new silicon revision number 1.6
From: Christoph Fritz @ 2018-01-09 12:51 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds new silicon revision number 1.6 as specified in document
IMX6DQCEC.pdf.
Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
---
arch/arm/mach-imx/anatop.c | 4 ++++
include/soc/imx/revision.h | 1 +
2 files changed, 5 insertions(+)
diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c
index 649a84c..deb98b8 100644
--- a/arch/arm/mach-imx/anatop.c
+++ b/arch/arm/mach-imx/anatop.c
@@ -157,6 +157,10 @@ void __init imx_init_revision_from_anatop(void)
*/
revision = IMX_CHIP_REVISION_1_5;
break;
+ case 6:
+ /* marked as 'E' in part number last character */
+ revision = IMX_CHIP_REVISION_1_6;
+ break;
default:
/*
* Fail back to return raw register value instead of 0xff.
diff --git a/include/soc/imx/revision.h b/include/soc/imx/revision.h
index 9ea3469..cd4c847 100644
--- a/include/soc/imx/revision.h
+++ b/include/soc/imx/revision.h
@@ -15,6 +15,7 @@
#define IMX_CHIP_REVISION_1_3 0x13
#define IMX_CHIP_REVISION_1_4 0x14
#define IMX_CHIP_REVISION_1_5 0x15
+#define IMX_CHIP_REVISION_1_6 0x16
#define IMX_CHIP_REVISION_2_0 0x20
#define IMX_CHIP_REVISION_2_1 0x21
#define IMX_CHIP_REVISION_2_2 0x22
--
2.1.4
^ permalink raw reply related
* [RFC PATCH] drivers: soc: xilinx: Add ZynqMP power domain driver
From: Philippe Ombredanne @ 2018-01-09 12:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515449572-5398-1-git-send-email-jollys@xilinx.com>
Jolly,
On Mon, Jan 8, 2018 at 11:12 PM, Jolly Shah <jolly.shah@xilinx.com> wrote:
> The zynqmp-genpd driver communicates the usage requirements
> for logical power domains / devices to the platform FW.
> FW is responsible for choosing appropriate power states,
> taking Linux' usage information into account.
>
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
> ---
<snip>
> --- /dev/null
> +++ b/drivers/soc/xilinx/zynqmp/pm_domains.c
> @@ -0,0 +1,343 @@
> +/*
> + * ZynqMP Generic PM domain support
> + *
> + * Copyright (C) 2014-2017 Xilinx, Inc.
> + *
> + * Davorin Mista <davorin.mista@aggios.com>
> + * Jolly Shah <jollys@xilinx.com>
> + * Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
This tag should be on the fist line as this:
// SPDX-License-Identifier: GPL-2.0+
--
Cordially
Philippe Ombredanne
^ permalink raw reply
* [PATCH v2] arm64: Branch predictor hardening for Cavium ThunderX2
From: Jayachandran C @ 2018-01-09 12:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180108164651.GQ25869@arm.com>
Use PSCI based mitigation for speculative execution attacks targeting
the branch predictor. The approach is similar to the one used for
Cortex-A CPUs, but in case of ThunderX2 we add another SMC call to
test if the firmware supports the capability.
If the secure firmware has been updated with the mitigation code to
invalidate the branch target buffer, we use the PSCI version call to
invoke it.
Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
---
v2:
- rebased on top of the latest kpti branch
- use pr_info_once/pr_warn_once to avoid excessive prints
- using .desc generated too many prints, dropped plan for using it
- fixed up a return
arch/arm64/kernel/cpu_errata.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 70e5f18..c626914 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -128,6 +128,7 @@ static void install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry,
__install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end);
}
+#include <linux/arm-smccc.h>
#include <linux/psci.h>
static int enable_psci_bp_hardening(void *data)
@@ -165,6 +166,33 @@ static int qcom_enable_link_stack_sanitization(void *data)
return 0;
}
+
+#define CAVIUM_TX2_SIP_SMC_CALL 0xC200FF00
+#define CAVIUM_TX2_BTB_HARDEN_CAP 0xB0A0
+
+static int enable_tx2_psci_bp_hardening(void *data)
+{
+ const struct arm64_cpu_capabilities *entry = data;
+ struct arm_smccc_res res;
+
+ if (!entry->matches(entry, SCOPE_LOCAL_CPU))
+ return 0;
+
+ arm_smccc_smc(CAVIUM_TX2_SIP_SMC_CALL, CAVIUM_TX2_BTB_HARDEN_CAP, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 != 0) {
+ pr_warn_once("Error: CONFIG_HARDEN_BRANCH_PREDICTOR enabled, but firmware does not support it\n");
+ return 0;
+ }
+ if (res.a1 == 1 && psci_ops.get_version) {
+ pr_info_once("Branch predictor hardening: Enabled, using PSCI version call.\n");
+ install_bp_hardening_cb(entry,
+ (bp_hardening_cb_t)psci_ops.get_version,
+ __psci_hyp_bp_inval_start,
+ __psci_hyp_bp_inval_end);
+ }
+
+ return 0;
+}
#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */
#define MIDR_RANGE(model, min, max) \
@@ -338,6 +366,16 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
.capability = ARM64_HARDEN_BP_POST_GUEST_EXIT,
MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR_V1),
},
+ {
+ .capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+ MIDR_ALL_VERSIONS(MIDR_BRCM_VULCAN),
+ .enable = enable_tx2_psci_bp_hardening,
+ },
+ {
+ .capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+ MIDR_ALL_VERSIONS(MIDR_CAVIUM_THUNDERX2),
+ .enable = enable_tx2_psci_bp_hardening,
+ },
#endif
{
}
--
2.7.4
^ permalink raw reply related
* [PATCH 6/9] arm64: dts: exynos: Add iRAM device-tree node for Exynos5433
From: Krzysztof Kozlowski @ 2018-01-09 12:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515484746-10656-7-git-send-email-cw00.choi@samsung.com>
On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the iRAM device-tree node of Exynos5433 which
> defines the memory map of iRAM as following and it is used for suspend.
> - address: 0x0202_0000 ~ 0x3000_0000
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
> arch/arm64/boot/dts/exynos/exynos5433.dtsi | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> index 62f276970174..77f4321b247c 100644
> --- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> @@ -262,6 +262,20 @@
> interrupt-affinity = <&cpu4>, <&cpu5>, <&cpu6>, <&cpu7>;
> };
>
> + sysram at 02020000 {
Remove leading 0.
Best regards,
Krzysztof
> + compatible = "mmio-sram";
> + reg = <0x02020000 0x5c000>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges = <0 0x02020000 0x5c000>;
> + status = "okay";
> +
> + smp-sysram at 5b000 {
> + compatible = "samsung,exynos4210-sysram-ns";
> + reg = <0x5b000 0x1000>;
> + };
> + };
> +
> chipid at 10000000 {
> compatible = "samsung,exynos4210-chipid";
> reg = <0x10000000 0x100>;
> --
> 1.9.1
>
^ permalink raw reply
* [PATCH] drivers: firmware: xilinx: Add ZynqMP firmware driver
From: Philippe Ombredanne @ 2018-01-09 12:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515449227-5096-1-git-send-email-jollys@xilinx.com>
Jolly,
On Mon, Jan 8, 2018 at 11:07 PM, Jolly Shah <jolly.shah@xilinx.com> wrote:
> This patch is adding communication layer with firmware.
> Firmware driver provides an interface to firmware APIs.
> Interface APIs can be used by any driver to communicate to
> PMUFW(Platform Management Unit). All requests go through ATF.
> Firmware-debug provides debugfs interface to all APIs.
> Firmware-ggs provides read/write interface to
> global storage registers.
>
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
<snip>
> --- /dev/null
> +++ b/drivers/firmware/xilinx/Kconfig
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0+
Thank you++ for using the SPDX tags: here is my cheerful ack for this:
Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
^ permalink raw reply
* [RFC PATCH 5/9] soc: samsung: pm: Add support for suspend-to-ram of Exynos5433
From: Krzysztof Kozlowski @ 2018-01-09 12:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515484746-10656-6-git-send-email-cw00.choi@samsung.com>
On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the specific exynos_pm_data instance for Exynos5433
> in order to support the suspend-to-ram. Exynos5433 SoC need to write
> the 'cpu_resume' poiter address and the specific magic number
> for suspend mode.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
> drivers/soc/samsung/exynos-pm.c | 38 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
>
> diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
> index 45d84bbe5e61..70d949ba5cab 100644
> --- a/drivers/soc/samsung/exynos-pm.c
> +++ b/drivers/soc/samsung/exynos-pm.c
> @@ -12,6 +12,7 @@
> #include <linux/regulator/machine.h>
> #include <linux/syscore_ops.h>
> #include <linux/suspend.h>
> +#include <linux/psci.h>
>
> #include <asm/cpuidle.h>
> #include <asm/io.h>
> @@ -123,7 +124,44 @@ static void exynos_pm_finish(void)
> #define exynos_pm_data_arm_ptr(data) NULL
> #endif
>
> +static int exynos5433_pm_suspend(unsigned long unused)
> +{
> + /*
> + * Exynos5433 uses PSCI v0.1 which provides the only one
> + * entry point (psci_ops.cpu_suspend) for both cpuidle and
> + * suspend-to-RAM. Also, PSCI v0.1 needs the specific 'power_state'
> + * parameter for the suspend mode. In order to enter suspend mode,
> + * Exynos5433 calls the 'psci_ops.cpu_suspend' with '0x3010000'
> + * power_state parameter.
> + *
> + * '0x3010000' means that both cluster and system are going to enter
> + * the power-down state as following:
> + * - [25:24] 0x3 : Indicate the cluster and system.
> + * - [16] 0x1 : Indicate power-down state.
Define them, it will be more readable (name of define given just as an example):
EXYNOS5433_PCSI_SUSPEND_SYSTEM (0x3 << 24)
(or even split per system and cluser and use BIT() if this is real meaning)
and probably reuse existing S5P_CENTRAL_LOWPWR_CFG
> + */
> + return psci_ops.cpu_suspend(0x3010000, __pa_symbol(cpu_resume));
> +}
> +
> +static int exynos5433_pm_suspend_enter(suspend_state_t state)
> +{
> + if (!sysram_ns_base_addr)
> + return -EINVAL;
> +
> + __raw_writel(virt_to_phys(cpu_resume), sysram_ns_base_addr + 0x8);
> + __raw_writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + 0xc);
Document them in Documentation/arm/Samsung/Bootloader-interface.txt.
Best regards,
Krzysztof
> +
> + return cpu_suspend(0, exynos5433_pm_suspend);
> +}
> +
> +const struct exynos_pm_data exynos5433_pm_data = {
> + .enter = exynos5433_pm_suspend_enter,
> +};
> +
> static const struct of_device_id exynos_pm_of_device_ids[] = {
> + {
> + .compatible = "samsung,exynos5433-pmu",
> + .data = exynos_pm_data_arm_ptr(exynos5433_pm_data),
> + },
> { /*sentinel*/ },
> };
>
> --
> 1.9.1
>
^ permalink raw reply
* [RFC PATCH 4/9] soc: samsung: Add generic power-management driver for Exynos
From: Krzysztof Kozlowski @ 2018-01-09 12:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515484746-10656-5-git-send-email-cw00.choi@samsung.com>
On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> To enter suspend, Exynos SoC requires the some machine dependent procedures.
> This patch introduces the generic power-management driver to support
> those requirements and generic interface for power state management.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
> arch/arm/mach-exynos/common.h | 1 -
> arch/arm/mach-exynos/exynos.c | 23 +----
> drivers/soc/samsung/Makefile | 2 +-
> drivers/soc/samsung/exynos-pm.c | 176 ++++++++++++++++++++++++++++++++++
> include/linux/soc/samsung/exynos-pm.h | 21 ++++
> 5 files changed, 199 insertions(+), 24 deletions(-)
> create mode 100644 drivers/soc/samsung/exynos-pm.c
> create mode 100644 include/linux/soc/samsung/exynos-pm.h
>
> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
> index afbc143a3d5d..ad482c0fc131 100644
> --- a/arch/arm/mach-exynos/common.h
> +++ b/arch/arm/mach-exynos/common.h
> @@ -119,7 +119,6 @@ enum {
> * Magic values for bootloader indicating chosen low power mode.
> * See also Documentation/arm/Samsung/Bootloader-interface.txt
> */
> -#define EXYNOS_SLEEP_MAGIC 0x00000bad
> #define EXYNOS_AFTR_MAGIC 0xfcba0d10
>
> void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
> diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
> index fbd108ce8745..0d5265d175c4 100644
> --- a/arch/arm/mach-exynos/exynos.c
> +++ b/arch/arm/mach-exynos/exynos.c
> @@ -12,6 +12,7 @@
> #include <linux/of_fdt.h>
of_address.h might be not needed anymore.
> #include <linux/platform_device.h>
> #include <linux/irqchip.h>
> +#include <linux/soc/samsung/exynos-pm.h>
> #include <linux/soc/samsung/exynos-regs-pmu.h>
>
> #include <asm/cacheflush.h>
> @@ -41,28 +42,6 @@
> .id = -1,
> };
>
> -void __iomem *sysram_base_addr __ro_after_init;
> -void __iomem *sysram_ns_base_addr __ro_after_init;
> -
> -void __init exynos_sysram_init(void)
> -{
> - struct device_node *node;
> -
> - for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
> - if (!of_device_is_available(node))
> - continue;
> - sysram_base_addr = of_iomap(node, 0);
> - break;
> - }
> -
> - for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
> - if (!of_device_is_available(node))
> - continue;
> - sysram_ns_base_addr = of_iomap(node, 0);
> - break;
> - }
> -}
> -
> static void __init exynos_init_late(void)
> {
> if (of_machine_is_compatible("samsung,exynos5440"))
> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
> index d2e637339a45..58ca5bdabf1f 100644
> --- a/drivers/soc/samsung/Makefile
> +++ b/drivers/soc/samsung/Makefile
> @@ -1,5 +1,5 @@
> # SPDX-License-Identifier: GPL-2.0
> -obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
> +obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos-pm.o
>
> obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
> exynos5250-pmu.o exynos5420-pmu.o \
> diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
> new file mode 100644
> index 000000000000..45d84bbe5e61
> --- /dev/null
> +++ b/drivers/soc/samsung/exynos-pm.c
> @@ -0,0 +1,176 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// based on arch/arm/mach-exynos/suspend.c
> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
> +//
> +// Exynos Power Management support driver
> +
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_fdt.h>
> +#include <linux/kernel.h>
> +#include <linux/regulator/machine.h>
> +#include <linux/syscore_ops.h>
> +#include <linux/suspend.h>
> +
> +#include <asm/cpuidle.h>
> +#include <asm/io.h>
> +#include <asm/suspend.h>
> +
> +#include <linux/soc/samsung/exynos-pm.h>
> +#include <linux/soc/samsung/exynos-pmu.h>
> +
> +/*
> + * The struct exynos_pm_data contains the callbacks of
> + * both struct platform_suspend_ops and syscore_ops.
> + * This structure is listed according to the call order,
> + * because the callback call order for the two structures is mixed.
> + */
> +struct exynos_pm_data {
> + int (*prepare)(void); /* for platform_suspend_ops */
> + int (*suspend)(void); /* for syscore_ops */
> + int (*enter)(suspend_state_t state); /* for platform_suspend_ops */
> + void (*resume)(void); /* for syscore_ops */
> + void (*finish)(void); /* for platform_suspend_ops */
> +};
> +
> +static struct platform_suspend_ops exynos_pm_suspend_ops;
> +static struct syscore_ops exynos_pm_syscore_ops;
> +static const struct exynos_pm_data *pm_data __ro_after_init;
It is already const, so __initconst?
> +
> +void __iomem *sysram_base_addr __ro_after_init;
> +void __iomem *sysram_ns_base_addr __ro_after_init;
> +
> +static int exynos_pm_prepare(void)
> +{
> + int ret;
> +
> + /*
> + * REVISIT: It would be better if struct platform_suspend_ops
> + * .prepare handler get the suspend_state_t as a parameter to
> + * avoid hard-coding the suspend to mem state. It's safe to do
> + * it now only because the suspend_valid_only_mem function is
> + * used as the .valid callback used to check if a given state
> + * is supported by the platform anyways.
> + */
> + ret = regulator_suspend_prepare(PM_SUSPEND_MEM);
> + if (ret) {
> + pr_err("Failed to prepare regulators for suspend (%d)\n", ret);
> + return ret;
> + }
> +
> + if (pm_data->prepare) {
> + ret = pm_data->prepare();
> + if (ret) {
> + pr_err("Failed to prepare for suspend (%d)\n", ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int exynos_pm_suspend(void)
> +{
> + if (pm_data->suspend)
> + return pm_data->suspend();
> +
> + return 0;
> +}
> +
> +static int exynos_pm_enter(suspend_state_t state)
> +{
> + int ret;
> +
> + exynos_sys_powerdown_conf(SYS_SLEEP);
> +
> + ret = pm_data->enter(state);
> + if (ret) {
> + pr_err("Failed to enter sleep\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void exynos_pm_resume(void)
> +{
> + exynos_sys_powerup_conf(SYS_SLEEP);
> +
> + if (pm_data->resume)
> + pm_data->resume();
> +}
> +
> +static void exynos_pm_finish(void)
> +{
> + int ret;
> +
> + ret = regulator_suspend_finish();
> + if (ret)
> + pr_warn("Failed to resume regulators from suspend (%d)\n", ret);
> +
> + if (pm_data->finish)
> + pm_data->finish();
> +}
> +
> +/*
> + * Split the data between ARM architectures because it is relatively big
> + * and useless on other arch.
> + */
> +#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
> +#define exynos_pm_data_arm_ptr(data) (&data)
> +#else
> +#define exynos_pm_data_arm_ptr(data) NULL
> +#endif
> +
> +static const struct of_device_id exynos_pm_of_device_ids[] = {
> + { /*sentinel*/ },
> +};
> +
> +void __init exynos_sysram_init(void)
> +{
> + struct device_node *np;
> +
> + for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram") {
> + if (!of_device_is_available(np))
> + continue;
> + sysram_base_addr = of_iomap(np, 0);
> + break;
> + }
> +
> + for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram-ns") {
> + if (!of_device_is_available(np))
> + continue;
> + sysram_ns_base_addr = of_iomap(np, 0);
> + break;
> + }
> +}
> +
> +static int __init exynos_pm_init(void)
> +{
> + const struct of_device_id *match;
> + struct device_node *np;
> +
> + np = of_find_matching_node_and_match(NULL,
> + exynos_pm_of_device_ids, &match);
> + if (!np) {
> + pr_err("Failed to find PMU node for Exynos Power-Management\n");
> + return -ENODEV;
> + }
> + pm_data = (const struct exynos_pm_data *) match->data;
> +
> + exynos_sysram_init();
> +
> + exynos_pm_suspend_ops.valid = suspend_valid_only_mem;
> + exynos_pm_suspend_ops.prepare = exynos_pm_prepare;
> + exynos_pm_syscore_ops.suspend = exynos_pm_suspend;
> + exynos_pm_suspend_ops.enter = exynos_pm_enter;
> + exynos_pm_syscore_ops.resume = exynos_pm_resume;
> + exynos_pm_suspend_ops.finish = exynos_pm_finish;
> +
> + register_syscore_ops(&exynos_pm_syscore_ops);
> + suspend_set_ops(&exynos_pm_suspend_ops);
> +
> + return 0;
> +}
> +postcore_initcall(exynos_pm_init);
As I mentioned in cover letter, please move here first ARMv7 code. Now
it looks like duplicating the existing code.
> diff --git a/include/linux/soc/samsung/exynos-pm.h b/include/linux/soc/samsung/exynos-pm.h
> new file mode 100644
> index 000000000000..b1afe95ed10c
> --- /dev/null
> +++ b/include/linux/soc/samsung/exynos-pm.h
> @@ -0,0 +1,21 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
> +//
> +// Header for Exynos Power-Management support driver
Use header-style SPDX and comment.
Best regards,
Krzysztof
> +
> +#ifndef __LINUX_SOC_EXYNOS_PM_H
> +#define __LINUX_SOC_EXYNOS_PM_H
> +
> +/*
> + * Magic values for bootloader indicating chosen low power mode.
> + * See also Documentation/arm/Samsung/Bootloader-interface.txt
> + */
> +#define EXYNOS_SLEEP_MAGIC 0x00000bad
> +
> +extern void __iomem *sysram_base_addr;
> +extern void __iomem *sysram_ns_base_addr;
Since these are now global symbols, they need nice exynos prefix.
Also, probably they should not be globally modifiable. Only
exynos_sysram_init() should write there. Instead export a global
accessor (get()) and rest should use that one.
Best regards,
Krzysztof
> +
> +extern void exynos_sysram_init(void);
> +
> +#endif /* __LINUX_SOC_EXYNOS_PMU_H */
> --
> 1.9.1
>
^ permalink raw reply
* [PATCH 08/19] drm/rcar-du: Convert to the new generic alpha property
From: Laurent Pinchart @ 2018-01-09 12:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f29de1e6964c393afaa21564d73425139a546672.1515494838.git-series.maxime.ripard@free-electrons.com>
Hi Maxime,
Thank you for the patch.
On Tuesday, 9 January 2018 12:56:27 EET Maxime Ripard wrote:
> Now that we have support for per-plane alpha in the core, let's use it.
>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> drivers/gpu/drm/rcar-du/rcar_du_drv.h | 1 +-
> drivers/gpu/drm/rcar-du/rcar_du_kms.c | 5 +---
> drivers/gpu/drm/rcar-du/rcar_du_plane.c | 15 +++------
> drivers/gpu/drm/rcar-du/rcar_du_plane.h | 2 +-
> drivers/gpu/drm/rcar-du/rcar_du_vsp.c | 42 ++------------------------
> drivers/gpu/drm/rcar-du/rcar_du_vsp.h | 2 +-
> 6 files changed, 9 insertions(+), 58 deletions(-)
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index f8cd79488ece..aff04adaae53
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> @@ -89,7 +89,6 @@ struct rcar_du_device {
> struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
>
> struct {
> - struct drm_property *alpha;
> struct drm_property *colorkey;
> } props;
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 566d1a948c8f..e1b5a7b460cc
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -417,11 +417,6 @@ static int rcar_du_encoders_init(struct rcar_du_device
> *rcdu)
>
> static int rcar_du_properties_init(struct rcar_du_device *rcdu)
> {
> - rcdu->props.alpha =
> - drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
> - if (rcdu->props.alpha == NULL)
> - return -ENOMEM;
> -
> /*
> * The color key is expressed as an RGB888 triplet stored in a 32-bit
> * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 61833cc1c699..5b34e8092c8b
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> @@ -423,7 +423,7 @@ static void rcar_du_plane_setup_mode(struct
> rcar_du_group *rgrp, rcar_du_plane_write(rgrp, index, PnALPHAR,
> PnALPHAR_ABIT_0);
> else
> rcar_du_plane_write(rgrp, index, PnALPHAR,
> - PnALPHAR_ABIT_X | state->alpha);
> + PnALPHAR_ABIT_X | state->state.alpha);
>
> pnmr = PnMR_BM_MD | state->format->pnmr;
>
> @@ -667,11 +667,11 @@ static void rcar_du_plane_reset(struct drm_plane
> *plane)
>
> state->hwindex = -1;
> state->source = RCAR_DU_PLANE_MEMORY;
> - state->alpha = 255;
> state->colorkey = RCAR_DU_COLORKEY_NONE;
> state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
>
> plane->state = &state->state;
> + plane->state->alpha = 255;
> plane->state->plane = plane;
> }
>
> @@ -683,9 +683,7 @@ static int rcar_du_plane_atomic_set_property(struct
> drm_plane *plane, struct rcar_du_plane_state *rstate =
> to_rcar_plane_state(state); struct rcar_du_device *rcdu =
> to_rcar_plane(plane)->group->dev;
>
> - if (property == rcdu->props.alpha)
> - rstate->alpha = val;
> - else if (property == rcdu->props.colorkey)
> + if (property == rcdu->props.colorkey)
> rstate->colorkey = val;
> else
> return -EINVAL;
> @@ -701,9 +699,7 @@ static int rcar_du_plane_atomic_get_property(struct
> drm_plane *plane, container_of(state, const struct rcar_du_plane_state,
> state);
> struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
>
> - if (property == rcdu->props.alpha)
> - *val = rstate->alpha;
> - else if (property == rcdu->props.colorkey)
> + if (property == rcdu->props.colorkey)
> *val = rstate->colorkey;
> else
> return -EINVAL;
> @@ -772,10 +768,9 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
> continue;
>
> drm_object_attach_property(&plane->plane.base,
> - rcdu->props.alpha, 255);
> - drm_object_attach_property(&plane->plane.base,
> rcdu->props.colorkey,
> RCAR_DU_COLORKEY_NONE);
> + drm_plane_create_alpha_property(&plane->plane, 255);
> drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
> }
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
> b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index f62e09f195de..2dc793ebd1a2
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
> @@ -50,7 +50,6 @@ static inline struct rcar_du_plane *to_rcar_plane(struct
> drm_plane *plane) * @state: base DRM plane state
> * @format: information about the pixel format used by the plane
> * @hwindex: 0-based hardware plane index, -1 means unused
> - * @alpha: value of the plane alpha property
> * @colorkey: value of the plane colorkey property
> */
> struct rcar_du_plane_state {
> @@ -60,7 +59,6 @@ struct rcar_du_plane_state {
> int hwindex;
> enum rcar_du_plane_source source;
>
> - unsigned int alpha;
> unsigned int colorkey;
> };
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index 2c96147bc444..ee85f6fdffad
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> @@ -54,6 +54,7 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
> };
> struct rcar_du_plane_state state = {
> .state = {
> + .alpha = 255,
> .crtc = &crtc->crtc,
> .crtc_x = 0,
> .crtc_y = 0,
> @@ -67,7 +68,6 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
> },
> .format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
> .source = RCAR_DU_PLANE_VSPD1,
> - .alpha = 255,
> .colorkey = 0,
> };
>
> @@ -173,7 +173,7 @@ static void rcar_du_vsp_plane_setup(struct
> rcar_du_vsp_plane *plane) struct vsp1_du_atomic_config cfg = {
> .pixelformat = 0,
> .pitch = fb->pitches[0],
> - .alpha = state->alpha,
> + .alpha = state->state.alpha,
> .zpos = state->state.zpos,
> };
> unsigned int i;
> @@ -351,44 +351,13 @@ static void rcar_du_vsp_plane_reset(struct drm_plane
> *plane) if (state == NULL)
> return;
>
> - state->alpha = 255;
> + state->state.alpha = 255;
> state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
>
> plane->state = &state->state;
> plane->state->plane = plane;
> }
>
> -static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
> - struct drm_plane_state *state, struct drm_property *property,
> - uint64_t val)
> -{
> - struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
> - struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
> -
> - if (property == rcdu->props.alpha)
> - rstate->alpha = val;
> - else
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> -static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
> - const struct drm_plane_state *state, struct drm_property *property,
> - uint64_t *val)
> -{
> - const struct rcar_du_vsp_plane_state *rstate =
> - container_of(state, const struct rcar_du_vsp_plane_state, state);
> - struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
> -
> - if (property == rcdu->props.alpha)
> - *val = rstate->alpha;
> - else
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
> .update_plane = drm_atomic_helper_update_plane,
> .disable_plane = drm_atomic_helper_disable_plane,
> @@ -396,8 +365,6 @@ static const struct drm_plane_funcs
> rcar_du_vsp_plane_funcs = { .destroy = drm_plane_cleanup,
> .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
> .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
> - .atomic_set_property = rcar_du_vsp_plane_atomic_set_property,
> - .atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
> };
>
> int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
> @@ -454,8 +421,7 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct
> device_node *np, if (type == DRM_PLANE_TYPE_PRIMARY)
> continue;
>
> - drm_object_attach_property(&plane->plane.base,
> - rcdu->props.alpha, 255);
> + drm_plane_create_alpha_property(&plane->plane, 255);
> drm_plane_create_zpos_property(&plane->plane, 1, 1,
> vsp->num_planes - 1);
> }
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
> b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h index f876c512163c..8b19914761e4
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
> @@ -44,7 +44,6 @@ static inline struct rcar_du_vsp_plane
> *to_rcar_vsp_plane(struct drm_plane *p) * @state: base DRM plane state
> * @format: information about the pixel format used by the plane
> * @sg_tables: scatter-gather tables for the frame buffer memory
> - * @alpha: value of the plane alpha property
> * @zpos: value of the plane zpos property
> */
> struct rcar_du_vsp_plane_state {
> @@ -53,7 +52,6 @@ struct rcar_du_vsp_plane_state {
> const struct rcar_du_format_info *format;
> struct sg_table sg_tables[3];
>
> - unsigned int alpha;
> unsigned int zpos;
> };
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH v5 01/44] dt-bindings: clock: Add new bindings for TI Davinci PLL clocks
From: Sekhar Nori @ 2018-01-09 12:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <22409e49-5c14-4068-b137-7535afaf90d7@lechnology.com>
On Monday 08 January 2018 09:59 PM, David Lechner wrote:
> On 01/08/2018 08:00 AM, Sekhar Nori wrote:
>> On Monday 08 January 2018 07:47 AM, David Lechner wrote:
>>> This adds a new binding for the PLL IP blocks in the mach-davinci family
>>> of processors. Currently, only the SYSCLKn and AUXCLK outputs are
>>> needed,
>>> but in the future additional child nodes could be added for OBSCLK and
>>> BPDIV.
>>>
>>> Note: Although these PLL controllers are very similar to the TI Keystone
>>> SoCs, we are not re-using those bindings. The Keystone bindings use a
>>> legacy one-node-per-clock binding. Furthermore, the mach-davinici SoCs
>>
>> Not sure what is meant by "legacy one-node-per-clock binding"
>
> It's a term I picked up from of_clk_detect_critical()
>
> ?* Do not use this function. It exists only for legacy Device Tree
> ?* bindings, such as the one-clock-per-node style that are outdated.
> ?* Those bindings typically put all clock data into .dts and the Linux
> ?* driver has no clock data, thus making it impossible to set this flag
> ?* correctly from the driver. Only those drivers may call
> ?* of_clk_detect_critical from their setup functions.
Okay, I still don't understand the outdated style. I looked at clocks
defined in arch/arm/boot/dts/stih407-clock.dtsi which is the only file
that uses clock-critical and don't particularly see anything wrong with
the way clocks are defined there.
Anyway, I guess we digress. As long as this patch series is not using
the "legacy style", we are good :)
>>> have a slightly different PLL register layout and a number of quirks
>>> that
>>> can't be handled by the existing bindings, so the keystone bindings
>>> could
>>> not be used as-is anyway.
>>
>> Right, I think different register layout between the processors is the
>> main reason for a new driver. This should be sufficient reason IMO.
>>
>>>
>>> Signed-off-by: David Lechner <david@lechnology.com>
>>> ---
>>> ? .../devicetree/bindings/clock/ti/davinci/pll.txt?? | 47
>>> ++++++++++++++++++++++
>>> ? 1 file changed, 47 insertions(+)
>>> ? create mode 100644
>>> Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>>
>>> diff --git
>>> a/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>> b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>> new file mode 100644
>>> index 0000000..99bf5da
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>> @@ -0,0 +1,47 @@
>>> +Binding for TI DaVinci PLL Controllers
>>> +
>>> +The PLL provides clocks to most of the components on the SoC. In
>>> addition
>>> +to the PLL itself, this controller also contains bypasses, gates,
>>> dividers,
>>> +an multiplexers for various clock signals.
>>> +
>>> +Required properties:
>>> +- compatible: shall be one of:
>>> +??? - "ti,da850-pll0" for PLL0 on DA850/OMAP-L138/AM18XX
>>> +??? - "ti,da850-pll1" for PLL1 on DA850/OMAP-L138/AM18XX
>>
>> These PLLs are same IP so they should use the same compatible. You can
>> initialize both PLLs for DA850 based on the same compatible.
>>
>
> But they are not exactly the same. For example, PLL0 has 7 PLLDIV clocks
> while
> PLL1 only has 3. PLL0 has PREDIV while PLL1 does not. PLL0 has certain
> SYSCLKs
> that are fixed-ratio but PLL1 does not have any of these. There are even
> more
> differences, but these are the ones we are actually using.
We need each element of the PLLC to be modeled individually as a clock
node. That is, PLL should only model the multiplier, the dividers
including post and prediv should be modeled as divider clocks (hopefully
being able to use the clk-divider.c library). The sysclks can be
fixed-factor-clock type clocks.
Without this flexible mechanism, we cannot (at least later) model things
like DIV4.5 clock which is the only clock which derives from the output
of PLL multiplier before the post divider is applied.
Since with DT there are are no retakes, we need to get this right the
first time and modifying later will not be an option.
>
> So, if we use the same compatible, we either have to come up with device
> tree
> bindings to describe all of this (yuck) or I suppose we can look at the
> REVID
> register to electronically determine exactly what we have. I went with the
> simpler option of just creating two different compatible strings.
Thanks,
Sekhar
^ permalink raw reply
* [PATCH 05/19] drm/vc4: Use the alpha format helper
From: Daniel Vetter @ 2018-01-09 12:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <78890e262e8394b2319f15c11de3282a2a2a2efd.1515494838.git-series.maxime.ripard@free-electrons.com>
On Tue, Jan 09, 2018 at 11:56:24AM +0100, Maxime Ripard wrote:
> Now that the core has a drm format helper to tell if a format embeds an
> alpha component in it, let's use it.
>
> Cc: Eric Anholt <eric@anholt.net>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
On patches 1-5:
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
> drivers/gpu/drm/vc4/vc4_plane.c | 19 +++++++++----------
> 1 file changed, 9 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
> index 423a23ed8fc2..2c0e25128dcd 100644
> --- a/drivers/gpu/drm/vc4/vc4_plane.c
> +++ b/drivers/gpu/drm/vc4/vc4_plane.c
> @@ -85,40 +85,39 @@ static const struct hvs_format {
> u32 drm; /* DRM_FORMAT_* */
> u32 hvs; /* HVS_FORMAT_* */
> u32 pixel_order;
> - bool has_alpha;
> bool flip_cbcr;
> } hvs_formats[] = {
> {
> .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> - .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
> + .pixel_order = HVS_PIXEL_ORDER_ABGR,
> },
> {
> .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> - .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
> + .pixel_order = HVS_PIXEL_ORDER_ABGR,
> },
> {
> .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> - .pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = true,
> + .pixel_order = HVS_PIXEL_ORDER_ARGB,
> },
> {
> .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> - .pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = false,
> + .pixel_order = HVS_PIXEL_ORDER_ARGB,
> },
> {
> .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
> - .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
> + .pixel_order = HVS_PIXEL_ORDER_XRGB,
> },
> {
> .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
> - .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false,
> + .pixel_order = HVS_PIXEL_ORDER_XBGR,
> },
> {
> .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
> - .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
> + .pixel_order = HVS_PIXEL_ORDER_ABGR,
> },
> {
> .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
> - .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
> + .pixel_order = HVS_PIXEL_ORDER_ABGR,
> },
> {
> .drm = DRM_FORMAT_YUV422,
> @@ -601,7 +600,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
> /* Position Word 2: Source Image Size, Alpha Mode */
> vc4_state->pos2_offset = vc4_state->dlist_count;
> vc4_dlist_write(vc4_state,
> - VC4_SET_FIELD(format->has_alpha ?
> + VC4_SET_FIELD(drm_format_has_alpha(format->drm) ?
> SCALER_POS2_ALPHA_MODE_PIPELINE :
> SCALER_POS2_ALPHA_MODE_FIXED,
> SCALER_POS2_ALPHA_MODE) |
> --
> git-series 0.9.1
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply
* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Laurent Pinchart @ 2018-01-09 12:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5c765fc730d75cb362dc37bcdb3b3aeacc7bdb30.1515494838.git-series.maxime.ripard@free-electrons.com>
Hi Maxime,
Thank you for the patch.
On Tuesday, 9 January 2018 12:56:25 EET Maxime Ripard wrote:
> Some drivers duplicate the logic to create a property to store a per-plane
> alpha.
>
> Let's create a helper in order to move that to the core.
>
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> Documentation/gpu/kms-properties.csv | 2 +-
> drivers/gpu/drm/drm_atomic.c | 4 ++++-
> drivers/gpu/drm/drm_atomic_helper.c | 1 +-
> drivers/gpu/drm/drm_blend.c | 32 +++++++++++++++++++++++++++++-
> include/drm/drm_blend.h | 1 +-
> include/drm/drm_plane.h | 6 +++++-
> 6 files changed, 45 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/gpu/kms-properties.csv
> b/Documentation/gpu/kms-properties.csv index 927b65e14219..a3c3969c1992
> 100644
> --- a/Documentation/gpu/kms-properties.csv
> +++ b/Documentation/gpu/kms-properties.csv
> @@ -99,5 +99,5 @@ radeon,DVI-I,?coherent?,RANGE,"Min=0, Max=1",Connector,TBD
> ,,"""underscan vborder""",RANGE,"Min=0, Max=128",Connector,TBD
> ,Audio,?audio?,ENUM,"{ ""off"", ""on"", ""auto"" }",Connector,TBD
> ,FMT Dithering,?dither?,ENUM,"{ ""off"", ""on"" }",Connector,TBD
> -rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
> +,,"""alpha""",RANGE,"Min=0, Max=255",Plane,Opacity of the plane from
> transparent (0) to opaque (255) ,,"""colorkey""",RANGE,"Min=0,
> Max=0x01ffffff",Plane,TBD
I think more documentation is needed. You should explain how the property
operates and which formats it is applicable to. For instance you need to
clarify what happens for format that contain an alpha component.
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index c2da5585e201..ade18cf62c89 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -749,6 +749,8 @@ static int drm_atomic_plane_set_property(struct
> drm_plane *plane, state->src_w = val;
> } else if (property == config->prop_src_h) {
> state->src_h = val;
> + } else if (property == plane->alpha_property) {
> + state->alpha = val;
> } else if (property == plane->rotation_property) {
> if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
> return -EINVAL;
> @@ -810,6 +812,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> *val = state->src_w;
> } else if (property == config->prop_src_h) {
> *val = state->src_h;
> + } else if (property == plane->alpha_property) {
> + *val = state->alpha;
> } else if (property == plane->rotation_property) {
> *val = state->rotation;
> } else if (property == plane->zpos_property) {
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c index 71d712f1b56a..018993df4c18
> 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3372,6 +3372,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane
> *plane)
>
> if (plane->state) {
> plane->state->plane = plane;
> + plane->state->alpha = 255;
If you keep the ability to select an initial value other than fully opaque
(see my comment below about that) you should reset to that value instead of
hardcoding 255.
> plane->state->rotation = DRM_MODE_ROTATE_0;
> }
> }
> diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c
> index 2e5e089dd912..8eea2a8af458 100644
> --- a/drivers/gpu/drm/drm_blend.c
> +++ b/drivers/gpu/drm/drm_blend.c
> @@ -104,6 +104,38 @@
> */
>
> /**
> + * drm_plane_create_alpha_property - create a new alpha property
> + * @plane: drm plane
> + * @alpha: initial value of alpha, from 0 (transparent) to 255 (opaque)
Do you have a use case for initializing the alpha value to something else than
fully opaque ?
> + * This function initializes a generic, mutable, alpha property and
> + * enables support for it in the DRM core.
> + *
> + * Drivers can then attach this property to their plane to enable
> + * support for configurable plane alpha.
The function attaches the property to the plane, is the documentation outdated
?
> + * Returns:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha)
> +{
> + struct drm_property *prop;
> +
> + prop = drm_property_create_range(plane->dev, 0, "alpha", 0, 255);
Do you think the 0-255 range will fit all use cases ?
> + if (!prop)
> + return -ENOMEM;
> +
> + drm_object_attach_property(&plane->base, prop, alpha);
> + plane->alpha_property = prop;
> +
> + if (plane->state)
> + plane->state->alpha = alpha;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_create_alpha_property);
> +
> +/**
> * drm_plane_create_rotation_property - create a new rotation property
> * @plane: drm plane
> * @rotation: initial value of the rotation property
> diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
> index 17606026590b..5979a8fce453 100644
> --- a/include/drm/drm_blend.h
> +++ b/include/drm/drm_blend.h
> @@ -36,6 +36,7 @@ static inline bool drm_rotation_90_or_270(unsigned int
> rotation) return rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
> }
>
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha);
> int drm_plane_create_rotation_property(struct drm_plane *plane,
> unsigned int rotation,
> unsigned int supported_rotations);
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 571615079230..a5e26064b132 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -42,6 +42,7 @@ struct drm_modeset_acquire_ctx;
> * plane (in 16.16)
> * @src_w: width of visible portion of plane (in 16.16)
> * @src_h: height of visible portion of plane (in 16.16)
> + * @alpha: opacity of the plane
> * @rotation: rotation of the plane
> * @zpos: priority of the given plane on crtc (optional)
> * Note that multiple active planes on the same crtc can have an identical
> @@ -105,6 +106,9 @@ struct drm_plane_state {
> uint32_t src_x, src_y;
> uint32_t src_h, src_w;
>
> + /* Plane opacity */
> + u8 alpha;
> +
> /* Plane rotation */
> unsigned int rotation;
>
> @@ -481,6 +485,7 @@ enum drm_plane_type {
> * @funcs: helper functions
> * @properties: property tracking for this plane
> * @type: type of plane (overlay, primary, cursor)
> + * @alpha_property: alpha property for this plane
> * @zpos_property: zpos property for this plane
> * @rotation_property: rotation property for this plane
> * @helper_private: mid-layer private data
> @@ -546,6 +551,7 @@ struct drm_plane {
> */
> struct drm_plane_state *state;
>
> + struct drm_property *alpha_property;
> struct drm_property *zpos_property;
> struct drm_property *rotation_property;
> };
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 3/9] soc: samsung: pmu: Add the PMU data of exynos5433 to support low-power state
From: Krzysztof Kozlowski @ 2018-01-09 12:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515484746-10656-4-git-send-email-cw00.choi@samsung.com>
On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the PMU (Power Management Unit) data of exynos5433 SoC
> in order to support the various power modes. Each power mode has
> the different value for reducing the power-consumption.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
> arch/arm/mach-exynos/common.h | 2 -
> drivers/soc/samsung/Makefile | 3 +-
> drivers/soc/samsung/exynos-pmu.c | 1 +
> drivers/soc/samsung/exynos-pmu.h | 2 +
> drivers/soc/samsung/exynos5433-pmu.c | 286 ++++++++++++++++++++++++++++
> include/linux/soc/samsung/exynos-regs-pmu.h | 148 ++++++++++++++
> 6 files changed, 439 insertions(+), 3 deletions(-)
> create mode 100644 drivers/soc/samsung/exynos5433-pmu.c
>
> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
> index 098f84a149a3..afbc143a3d5d 100644
> --- a/arch/arm/mach-exynos/common.h
> +++ b/arch/arm/mach-exynos/common.h
> @@ -125,8 +125,6 @@ enum {
> void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
> void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode);
>
> -extern u32 exynos_get_eint_wake_mask(void);
> -
> #ifdef CONFIG_PM_SLEEP
> extern void __init exynos_pm_init(void);
> #else
> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
> index 29f294baac6e..d2e637339a45 100644
> --- a/drivers/soc/samsung/Makefile
> +++ b/drivers/soc/samsung/Makefile
> @@ -2,5 +2,6 @@
> obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
>
> obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
> - exynos5250-pmu.o exynos5420-pmu.o
> + exynos5250-pmu.o exynos5420-pmu.o \
> + exynos5433-pmu.o
... ah, I forgot. No. Exynos5433 is not ARMv7.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Daniel Vetter @ 2018-01-09 12:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5c765fc730d75cb362dc37bcdb3b3aeacc7bdb30.1515494838.git-series.maxime.ripard@free-electrons.com>
On Tue, Jan 09, 2018 at 11:56:25AM +0100, Maxime Ripard wrote:
> Some drivers duplicate the logic to create a property to store a per-plane
> alpha.
>
> Let's create a helper in order to move that to the core.
>
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Do we have userspace for this? Is encoding a fixed 0-255 range really the
best idea?
I know other drivers have skimped on the rules here a bit ... But at least
internally (i.e. within the drm_plane_state) we probably should restrict
ourselves to u8. And this needs real docs (i.e. the full blend equation
drivers are supposed to implement).
-Daniel
> ---
> Documentation/gpu/kms-properties.csv | 2 +-
> drivers/gpu/drm/drm_atomic.c | 4 ++++-
> drivers/gpu/drm/drm_atomic_helper.c | 1 +-
> drivers/gpu/drm/drm_blend.c | 32 +++++++++++++++++++++++++++++-
> include/drm/drm_blend.h | 1 +-
> include/drm/drm_plane.h | 6 +++++-
> 6 files changed, 45 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/gpu/kms-properties.csv b/Documentation/gpu/kms-properties.csv
> index 927b65e14219..a3c3969c1992 100644
> --- a/Documentation/gpu/kms-properties.csv
> +++ b/Documentation/gpu/kms-properties.csv
> @@ -99,5 +99,5 @@ radeon,DVI-I,?coherent?,RANGE,"Min=0, Max=1",Connector,TBD
> ,,"""underscan vborder""",RANGE,"Min=0, Max=128",Connector,TBD
> ,Audio,?audio?,ENUM,"{ ""off"", ""on"", ""auto"" }",Connector,TBD
> ,FMT Dithering,?dither?,ENUM,"{ ""off"", ""on"" }",Connector,TBD
> -rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
> +,,"""alpha""",RANGE,"Min=0, Max=255",Plane,Opacity of the plane from transparent (0) to opaque (255)
> ,,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index c2da5585e201..ade18cf62c89 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -749,6 +749,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
> state->src_w = val;
> } else if (property == config->prop_src_h) {
> state->src_h = val;
> + } else if (property == plane->alpha_property) {
> + state->alpha = val;
> } else if (property == plane->rotation_property) {
> if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
> return -EINVAL;
> @@ -810,6 +812,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> *val = state->src_w;
> } else if (property == config->prop_src_h) {
> *val = state->src_h;
> + } else if (property == plane->alpha_property) {
> + *val = state->alpha;
> } else if (property == plane->rotation_property) {
> *val = state->rotation;
> } else if (property == plane->zpos_property) {
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 71d712f1b56a..018993df4c18 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3372,6 +3372,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
>
> if (plane->state) {
> plane->state->plane = plane;
> + plane->state->alpha = 255;
> plane->state->rotation = DRM_MODE_ROTATE_0;
> }
> }
> diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c
> index 2e5e089dd912..8eea2a8af458 100644
> --- a/drivers/gpu/drm/drm_blend.c
> +++ b/drivers/gpu/drm/drm_blend.c
> @@ -104,6 +104,38 @@
> */
>
> /**
> + * drm_plane_create_alpha_property - create a new alpha property
> + * @plane: drm plane
> + * @alpha: initial value of alpha, from 0 (transparent) to 255 (opaque)
> + *
> + * This function initializes a generic, mutable, alpha property and
> + * enables support for it in the DRM core.
> + *
> + * Drivers can then attach this property to their plane to enable
> + * support for configurable plane alpha.
> + *
> + * Returns:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha)
> +{
> + struct drm_property *prop;
> +
> + prop = drm_property_create_range(plane->dev, 0, "alpha", 0, 255);
> + if (!prop)
> + return -ENOMEM;
> +
> + drm_object_attach_property(&plane->base, prop, alpha);
> + plane->alpha_property = prop;
> +
> + if (plane->state)
> + plane->state->alpha = alpha;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_create_alpha_property);
> +
> +/**
> * drm_plane_create_rotation_property - create a new rotation property
> * @plane: drm plane
> * @rotation: initial value of the rotation property
> diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
> index 17606026590b..5979a8fce453 100644
> --- a/include/drm/drm_blend.h
> +++ b/include/drm/drm_blend.h
> @@ -36,6 +36,7 @@ static inline bool drm_rotation_90_or_270(unsigned int rotation)
> return rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
> }
>
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha);
> int drm_plane_create_rotation_property(struct drm_plane *plane,
> unsigned int rotation,
> unsigned int supported_rotations);
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 571615079230..a5e26064b132 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -42,6 +42,7 @@ struct drm_modeset_acquire_ctx;
> * plane (in 16.16)
> * @src_w: width of visible portion of plane (in 16.16)
> * @src_h: height of visible portion of plane (in 16.16)
> + * @alpha: opacity of the plane
> * @rotation: rotation of the plane
> * @zpos: priority of the given plane on crtc (optional)
> * Note that multiple active planes on the same crtc can have an identical
> @@ -105,6 +106,9 @@ struct drm_plane_state {
> uint32_t src_x, src_y;
> uint32_t src_h, src_w;
>
> + /* Plane opacity */
> + u8 alpha;
> +
> /* Plane rotation */
> unsigned int rotation;
>
> @@ -481,6 +485,7 @@ enum drm_plane_type {
> * @funcs: helper functions
> * @properties: property tracking for this plane
> * @type: type of plane (overlay, primary, cursor)
> + * @alpha_property: alpha property for this plane
> * @zpos_property: zpos property for this plane
> * @rotation_property: rotation property for this plane
> * @helper_private: mid-layer private data
> @@ -546,6 +551,7 @@ struct drm_plane {
> */
> struct drm_plane_state *state;
>
> + struct drm_property *alpha_property;
> struct drm_property *zpos_property;
> struct drm_property *rotation_property;
> };
> --
> git-series 0.9.1
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply
* [PATCH 07/19] drm/atmel-hclcdc: Convert to the new generic alpha property
From: Boris Brezillon @ 2018-01-09 12:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87b4f265eaade03c5afdbd55a46851bbe4d8d2fe.1515494838.git-series.maxime.ripard@free-electrons.com>
On Tue, 9 Jan 2018 11:56:26 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> Now that we have support for per-plane alpha in the core, let's use it.
>
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 13 +---
> drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 89 ++----------------
> 2 files changed, 14 insertions(+), 88 deletions(-)
>
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> index 6833ee253cfa..704cac6399eb 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> @@ -298,7 +298,6 @@ struct atmel_hlcdc_layer {
> struct atmel_hlcdc_plane {
> struct drm_plane base;
> struct atmel_hlcdc_layer layer;
> - struct atmel_hlcdc_plane_properties *properties;
> };
>
> static inline struct atmel_hlcdc_plane *
> @@ -345,18 +344,6 @@ struct atmel_hlcdc_dc_desc {
> };
>
> /**
> - * Atmel HLCDC Plane properties.
> - *
> - * This structure stores plane property definitions.
> - *
> - * @alpha: alpha blending (or transparency) property
> - * @rotation: rotation property
> - */
> -struct atmel_hlcdc_plane_properties {
> - struct drm_property *alpha;
> -};
> -
> -/**
> * Atmel HLCDC Display Controller.
> *
> * @desc: HLCDC Display Controller description
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 1a9318810a29..dbc508889e87 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> @@ -31,7 +31,6 @@
> * @src_y: y buffer position
> * @src_w: buffer width
> * @src_h: buffer height
> - * @alpha: alpha blending of the plane
> * @disc_x: x discard position
> * @disc_y: y discard position
> * @disc_w: discard width
> @@ -54,8 +53,6 @@ struct atmel_hlcdc_plane_state {
> uint32_t src_w;
> uint32_t src_h;
>
> - u8 alpha;
> -
> int disc_x;
> int disc_y;
> int disc_w;
> @@ -385,7 +382,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
> cfg |= ATMEL_HLCDC_LAYER_LAEN;
> else
> cfg |= ATMEL_HLCDC_LAYER_GAEN |
> - ATMEL_HLCDC_LAYER_GA(state->alpha);
> + ATMEL_HLCDC_LAYER_GA(state->base.alpha);
> }
>
> if (state->disc_h && state->disc_w)
> @@ -553,7 +550,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
>
> if (!ovl_s->fb ||
> drm_format_has_alpha(ovl_s->fb->format->format) ||
> - ovl_state->alpha != 255)
> + ovl_s->alpha != 255)
> continue;
>
> /* TODO: implement a smarter hidden area detection */
> @@ -829,51 +826,18 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
> drm_plane_cleanup(p);
> }
>
> -static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
> - struct drm_plane_state *s,
> - struct drm_property *property,
> - uint64_t val)
> -{
> - struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> - struct atmel_hlcdc_plane_properties *props = plane->properties;
> - struct atmel_hlcdc_plane_state *state =
> - drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -
> - if (property == props->alpha)
> - state->alpha = val;
> - else
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> -static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
> - const struct drm_plane_state *s,
> - struct drm_property *property,
> - uint64_t *val)
> -{
> - struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> - struct atmel_hlcdc_plane_properties *props = plane->properties;
> - const struct atmel_hlcdc_plane_state *state =
> - container_of(s, const struct atmel_hlcdc_plane_state, base);
> -
> - if (property == props->alpha)
> - *val = state->alpha;
> - else
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> -static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
> - struct atmel_hlcdc_plane_properties *props)
> +static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)
> {
> const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
>
> if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> - desc->type == ATMEL_HLCDC_CURSOR_LAYER)
> - drm_object_attach_property(&plane->base.base,
> - props->alpha, 255);
> + desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
> + int ret;
> +
> + ret = drm_plane_create_alpha_property(&plane->base, 255);
> + if (ret)
> + return ret;
> + }
>
> if (desc->layout.xstride && desc->layout.pstride) {
> int ret;
> @@ -988,8 +952,8 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
> return;
> }
>
> - state->alpha = 255;
> p->state = &state->base;
> + p->state->alpha = 255;
> p->state->plane = p;
> }
> }
> @@ -1042,13 +1006,10 @@ static const struct drm_plane_funcs layer_plane_funcs = {
> .reset = atmel_hlcdc_plane_reset,
> .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
> .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
> - .atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
> - .atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
> };
>
> static int atmel_hlcdc_plane_create(struct drm_device *dev,
> - const struct atmel_hlcdc_layer_desc *desc,
> - struct atmel_hlcdc_plane_properties *props)
> + const struct atmel_hlcdc_layer_desc *desc)
> {
> struct atmel_hlcdc_dc *dc = dev->dev_private;
> struct atmel_hlcdc_plane *plane;
> @@ -1060,7 +1021,6 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
> return -ENOMEM;
>
> atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
> - plane->properties = props;
>
> if (desc->type == ATMEL_HLCDC_BASE_LAYER)
> type = DRM_PLANE_TYPE_PRIMARY;
> @@ -1081,7 +1041,7 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
> &atmel_hlcdc_layer_plane_helper_funcs);
>
> /* Set default property values*/
> - ret = atmel_hlcdc_plane_init_properties(plane, props);
> + ret = atmel_hlcdc_plane_init_properties(plane);
> if (ret)
> return ret;
>
> @@ -1090,34 +1050,13 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
> return 0;
> }
>
> -static struct atmel_hlcdc_plane_properties *
> -atmel_hlcdc_plane_create_properties(struct drm_device *dev)
> -{
> - struct atmel_hlcdc_plane_properties *props;
> -
> - props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
> - if (!props)
> - return ERR_PTR(-ENOMEM);
> -
> - props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
> - if (!props->alpha)
> - return ERR_PTR(-ENOMEM);
> -
> - return props;
> -}
> -
> int atmel_hlcdc_create_planes(struct drm_device *dev)
> {
> struct atmel_hlcdc_dc *dc = dev->dev_private;
> - struct atmel_hlcdc_plane_properties *props;
> const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
> int nlayers = dc->desc->nlayers;
> int i, ret;
>
> - props = atmel_hlcdc_plane_create_properties(dev);
> - if (IS_ERR(props))
> - return PTR_ERR(props);
> -
> dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
> sizeof(struct atmel_hlcdc_dma_channel_dscr),
> sizeof(u64), 0);
> @@ -1130,7 +1069,7 @@ int atmel_hlcdc_create_planes(struct drm_device *dev)
> descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
> continue;
>
> - ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
> + ret = atmel_hlcdc_plane_create(dev, &descs[i]);
> if (ret)
> return ret;
> }
^ permalink raw reply
* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Boris Brezillon @ 2018-01-09 12:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5c765fc730d75cb362dc37bcdb3b3aeacc7bdb30.1515494838.git-series.maxime.ripard@free-electrons.com>
On Tue, 9 Jan 2018 11:56:25 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> Some drivers duplicate the logic to create a property to store a per-plane
> alpha.
>
> Let's create a helper in order to move that to the core.
>
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> Documentation/gpu/kms-properties.csv | 2 +-
> drivers/gpu/drm/drm_atomic.c | 4 ++++-
> drivers/gpu/drm/drm_atomic_helper.c | 1 +-
> drivers/gpu/drm/drm_blend.c | 32 +++++++++++++++++++++++++++++-
> include/drm/drm_blend.h | 1 +-
> include/drm/drm_plane.h | 6 +++++-
> 6 files changed, 45 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/gpu/kms-properties.csv b/Documentation/gpu/kms-properties.csv
> index 927b65e14219..a3c3969c1992 100644
> --- a/Documentation/gpu/kms-properties.csv
> +++ b/Documentation/gpu/kms-properties.csv
> @@ -99,5 +99,5 @@ radeon,DVI-I,?coherent?,RANGE,"Min=0, Max=1",Connector,TBD
> ,,"""underscan vborder""",RANGE,"Min=0, Max=128",Connector,TBD
> ,Audio,?audio?,ENUM,"{ ""off"", ""on"", ""auto"" }",Connector,TBD
> ,FMT Dithering,?dither?,ENUM,"{ ""off"", ""on"" }",Connector,TBD
> -rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
> +,,"""alpha""",RANGE,"Min=0, Max=255",Plane,Opacity of the plane from transparent (0) to opaque (255)
> ,,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index c2da5585e201..ade18cf62c89 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -749,6 +749,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
> state->src_w = val;
> } else if (property == config->prop_src_h) {
> state->src_h = val;
> + } else if (property == plane->alpha_property) {
> + state->alpha = val;
> } else if (property == plane->rotation_property) {
> if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
> return -EINVAL;
> @@ -810,6 +812,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
> *val = state->src_w;
> } else if (property == config->prop_src_h) {
> *val = state->src_h;
> + } else if (property == plane->alpha_property) {
> + *val = state->alpha;
> } else if (property == plane->rotation_property) {
> *val = state->rotation;
> } else if (property == plane->zpos_property) {
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 71d712f1b56a..018993df4c18 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3372,6 +3372,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
>
> if (plane->state) {
> plane->state->plane = plane;
> + plane->state->alpha = 255;
> plane->state->rotation = DRM_MODE_ROTATE_0;
> }
> }
> diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c
> index 2e5e089dd912..8eea2a8af458 100644
> --- a/drivers/gpu/drm/drm_blend.c
> +++ b/drivers/gpu/drm/drm_blend.c
> @@ -104,6 +104,38 @@
> */
>
> /**
> + * drm_plane_create_alpha_property - create a new alpha property
> + * @plane: drm plane
> + * @alpha: initial value of alpha, from 0 (transparent) to 255 (opaque)
> + *
> + * This function initializes a generic, mutable, alpha property and
> + * enables support for it in the DRM core.
> + *
> + * Drivers can then attach this property to their plane to enable
> + * support for configurable plane alpha.
> + *
> + * Returns:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha)
> +{
> + struct drm_property *prop;
> +
> + prop = drm_property_create_range(plane->dev, 0, "alpha", 0, 255);
> + if (!prop)
> + return -ENOMEM;
> +
> + drm_object_attach_property(&plane->base, prop, alpha);
> + plane->alpha_property = prop;
> +
> + if (plane->state)
> + plane->state->alpha = alpha;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_create_alpha_property);
> +
> +/**
> * drm_plane_create_rotation_property - create a new rotation property
> * @plane: drm plane
> * @rotation: initial value of the rotation property
> diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
> index 17606026590b..5979a8fce453 100644
> --- a/include/drm/drm_blend.h
> +++ b/include/drm/drm_blend.h
> @@ -36,6 +36,7 @@ static inline bool drm_rotation_90_or_270(unsigned int rotation)
> return rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
> }
>
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha);
> int drm_plane_create_rotation_property(struct drm_plane *plane,
> unsigned int rotation,
> unsigned int supported_rotations);
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 571615079230..a5e26064b132 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -42,6 +42,7 @@ struct drm_modeset_acquire_ctx;
> * plane (in 16.16)
> * @src_w: width of visible portion of plane (in 16.16)
> * @src_h: height of visible portion of plane (in 16.16)
> + * @alpha: opacity of the plane
> * @rotation: rotation of the plane
> * @zpos: priority of the given plane on crtc (optional)
> * Note that multiple active planes on the same crtc can have an identical
> @@ -105,6 +106,9 @@ struct drm_plane_state {
> uint32_t src_x, src_y;
> uint32_t src_h, src_w;
>
> + /* Plane opacity */
> + u8 alpha;
> +
> /* Plane rotation */
> unsigned int rotation;
>
> @@ -481,6 +485,7 @@ enum drm_plane_type {
> * @funcs: helper functions
> * @properties: property tracking for this plane
> * @type: type of plane (overlay, primary, cursor)
> + * @alpha_property: alpha property for this plane
> * @zpos_property: zpos property for this plane
> * @rotation_property: rotation property for this plane
> * @helper_private: mid-layer private data
> @@ -546,6 +551,7 @@ struct drm_plane {
> */
> struct drm_plane_state *state;
>
> + struct drm_property *alpha_property;
> struct drm_property *zpos_property;
> struct drm_property *rotation_property;
> };
^ 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