Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v3 6/6] drm/rockchip: dw_hdmi: add dw-hdmi support for the rk3328
From: Rob Herring @ 2018-05-22 17:13 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: mark.rutland, Jose.Abreu, algea.cao, devicetree, dri-devel,
	Laurent.pinchart, linux-rockchip, linux-arm-kernel, zhengyang
In-Reply-To: <20180515134736.5824-7-heiko@sntech.de>

On Tue, May 15, 2018 at 03:47:36PM +0200, Heiko Stuebner wrote:
> The rk3328 uses a dw-hdmi controller with an external hdmi phy from
> Innosilicon which uses the generic phy framework for access.
> Add the necessary data and the compatible for the rk3328 to the
> rockchip dw-hdmi driver.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> Tested-by: Robin Murphy <robin.murphy@arm.com>
> ---
> changes in v3:
> - reword as suggested by Rob to show that it's a dw-hdmi + Inno phy
> 
>  .../display/rockchip/dw_hdmi-rockchip.txt     |   1 +

Acked-by: Rob Herring <robh@kernel.org>

>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c   | 106 ++++++++++++++++++
>  2 files changed, 107 insertions(+)
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* Re: [PATCH/RFC v4 2/4] usb: gadget: udc: renesas_usb3: Add register of usb role switch
From: Rob Herring @ 2018-05-22 17:13 UTC (permalink / raw)
  To: Yoshihiro Shimoda
  Cc: gregkh, mark.rutland, heikki.krogerus, hdegoede, andy.shevchenko,
	linux-usb, linux-renesas-soc, devicetree
In-Reply-To: <1526990469-20739-3-git-send-email-yoshihiro.shimoda.uh@renesas.com>

On Tue, May 22, 2018 at 09:01:07PM +0900, Yoshihiro Shimoda wrote:
> This patch adds role switch support for R-Car SoCs into the USB 3.0
> peripheral driver. Some R-Car SoCs (e.g. R-Car H3) have USB 3.0
> dual-role device controller which has the USB 3.0 xHCI host and
> Renesas USB 3.0 peripheral.
> 
> Unfortunately, the mode change register contains the USB 3.0 peripheral
> controller side only. So, the USB 3.0 peripheral driver (renesas_usb3)
> manages this register now. However, in peripheral mode, the host
> should stop. Also the host hardware needs to reinitialize its own
> registers when the mode changes from peripheral to host mode.
> Otherwise, the host cannot work correctly (e.g. detect a device as
> high-speed).
> 
> To achieve this by a driver, this role switch driver manages
> the mode change register and attach/release the xhci-plat driver.
> 
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
> ---
>  .../devicetree/bindings/usb/renesas_usb3.txt       | 15 ++++

Please split bindings to a separate patch.

>  drivers/usb/gadget/udc/Kconfig                     |  1 +
>  drivers/usb/gadget/udc/renesas_usb3.c              | 82 ++++++++++++++++++++++
>  3 files changed, 98 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/usb/renesas_usb3.txt b/Documentation/devicetree/bindings/usb/renesas_usb3.txt
> index 2c071bb5..f6105aa 100644
> --- a/Documentation/devicetree/bindings/usb/renesas_usb3.txt
> +++ b/Documentation/devicetree/bindings/usb/renesas_usb3.txt
> @@ -19,6 +19,9 @@ Required properties:
>  Optional properties:
>    - phys: phandle + phy specifier pair
>    - phy-names: must be "usb"
> +  - The connection to a usb3.0 host node needs by using OF graph bindings for
> +    usb role switch.
> +   - port@0 = USB3.0 host port.

On the host side, this might conflict with the USB connector binding. 

I would either make sure this can work with the connector binding by 
having 2 endpoints on the HS or SS port or just use the 'companion' 
property defined in usb-generic.txt.

Rob

^ permalink raw reply

* Re: [PATCH v6 3/9] docs: Add Generic Counter interface documentation
From: Jonathan Cameron @ 2018-05-22 17:08 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, fabrice.gasnier, linux-iio, linux-kernel,
	devicetree, linux-arm-kernel
In-Reply-To: <20180521134729.GB5723@sophia>

...
> >> +
> >> +COUNT
> >> +-----
> >> +A Count represents the count data for a set of Signals. The Generic
> >> +Counter interface provides the following available count data types:
> >> +
> >> +* COUNT_POSITION_UNSIGNED:
> >> +  Unsigned integer value representing position.
> >> +
> >> +* COUNT_POSITION_SIGNED:
> >> +  Signed integer value representing position.  
> >
> >Just a thought: As the '0' position is effectively arbitrary is there any
> >actual difference between signed and unsigned? If we defined all counters
> >to be unsigned and just offset any signed ones so the range still fitted
> >would we end up with a simpler interface - there would be no real loss
> >of meaning that I can see..  I suppose it might not be what people expect
> >though when they see their counters start at a large positive value...  
> 
> This is something I've been on the fence about for a while. I would
> actually prefer the interface to have simply a COUNT_POSITION data type
> to represent position and leave it as unsigned; distinguishing between
> signed and unsigned position seems ultimately arbitrary given that it's
> mathematically just an offset as you have pointed out.
> 
> If we were to go down this route, then we'd have a count value that may
> always be represented using an unsigned data type, with an offset value
> that may always be represented using a signed data type -- the
> relationship being such: position = count + offset. Is that correct?

Given the positions are all arbitrary (such as limits etc) there is no
real need to have 0 as in anyway special.  So you could just apply an
offset to everything then don't make them visible to userspace at all.

> 
> My reason for giving the option for either signed or unsigned position
> was to help minimize the work userspace would have to do in order to get
> the value in which they're actually interested. I suppose it was a
> question of how abstract I want to make the interface -- although,
> making it simpler for userspace put more of a burden on the kernel side.
> 
> However, the "offset" value route may actually be more robust in the end
> because userspace would have to know whether they want a signed or
> unsigned position regardless in order to parse, so with count and offet
> defined we know they will always be unsigned and signed respectively.

Hmm. It's not obvious to me which is the best option.

> 
> >
> >
> >
> >  
> >> +
> >> +A Count has a count function mode which represents the update behavior
> >> +for the count data. The Generic Counter interface provides the following
> >> +available count function modes:
> >> +
> >> +* Increase:
> >> +  Accumulated count is incremented.
> >> +
> >> +* Decrease:
> >> +  Accumulated count is decremented.
> >> +
> >> +* Pulse-Direction:
> >> +  Rising edges on quadrature pair signal A updates the respective count.
> >> +  The input level of quadrature pair signal B determines direction.
> >> +  
> >Perhaps group the quadrature modes for the point of view of this document?
> >Might be slightly easier to read?  
> >
> >* Quadrature Modes
> >
> >  - x1 A: etc.
> >  
> >> +* Quadrature x1 A:
> >> +  If direction is forward, rising edges on quadrature pair signal A
> >> +  updates the respective count; if the direction is backward, falling
> >> +  edges on quadrature pair signal A updates the respective count.
> >> +  Quadrature encoding determines the direction.
> >> +
> >> +* Quadrature x1 B:
> >> +  If direction is forward, rising edges on quadrature pair signal B
> >> +  updates the respective count; if the direction is backward, falling
> >> +  edges on quadrature pair signal B updates the respective count.
> >> +  Quadrature encoding determines the direction.
> >> +
> >> +* Quadrature x2 A:
> >> +  Any state transition on quadrature pair signal A updates the
> >> +  respective count. Quadrature encoding determines the direction.
> >> +
> >> +* Quadrature x2 B:
> >> +  Any state transition on quadrature pair signal B updates the
> >> +  respective count. Quadrature encoding determines the direction.
> >> +
> >> +* Quadrature x2 Rising:
> >> +  Rising edges on either quadrature pair signals updates the respective
> >> +  count. Quadrature encoding determines the direction.  
> >
> >This one I've never met.  Really? There are devices who do this form
> >of crazy? It gives really uneven counting and I'm failing to see when
> >it would ever make sense...  References for these odd corner cases
> >would be good.
> >
> >
> >__|---|____|-----|____
> >____|----|____|-----|____
> >
> >001122222223334444444  
> 
> That's the same reaction I had when I discovered this -- in fact the
> STM32 LP Timer is the first time I've come across such a quadrature
> mode. I'm not sure of the use case for this mode, because positioning
> wouldn't be precise as you've pointed out. Perhaps Fabrice or Benjamin
> can probe the ST guys responsible for this design choice to figure out
> the rationale.

Hmm.  My inclination would be to not support it unless someone can up
with a meaningful use.  We are adding ABI (be it not much) for a case
that to us makes no sense.

Looks rather like the sort of thing that is a side effect of the
implementation rather than deliberate.

> 
> I'm leaving in these modes for now, as they do exist in the STM32 LP
> Timer, but it does make me curious what the intentions for them were
> (perhaps use cases outside of traditional quadrature encoder
> positioning).
> 

Sure if there is a usecase then fair enough.

Jonathan

^ permalink raw reply

* Re: [PATCH v6 5/5] drm/rockchip: support dp training outside dp firmware
From: Enric Balletbo Serra @ 2018-05-22 17:06 UTC (permalink / raw)
  To: hl
  Cc: devicetree@vger.kernel.org, David Airlie, linux-kernel,
	Brian Norris, Doug Anderson, Kishon Vijay Abraham I,
	open list:ARM/Rockchip SoC..., Rob Herring, dri-devel,
	Chris Zhong, daniel.vetter, Linux ARM
In-Reply-To: <CAFqH_50Gn7SuzJo2mDjGhsKC6_H=xMVKCbr2BM82geAyZaDu+A@mail.gmail.com>

Lin,

2018-05-22 9:41 GMT+02:00 Enric Balletbo Serra <eballetbo@gmail.com>:
> Hi Lin
>
> 2018-05-22 3:08 GMT+02:00 hl <hl@rock-chips.com>:
>> Hi Enric,
>>
>>
>>
>>
>> On Monday, May 21, 2018 11:22 PM, Enric Balletbo Serra wrote:
>>>
>>> Hi Lin,
>>>
>>> 2018-05-21 11:37 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>>>>
>>>> DP firmware uses fixed phy config values to do training, but some
>>>> boards need to adjust these values to fit for their unique hardware
>>>> design. So get phy config values from dts and use software link training
>>>> instead of relying on firmware, if software training fail, keep firmware
>>>> training as a fallback if sw training fails.
>>>>
>>>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>>>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>>>> Reviewed-by: Sean Paul <seanpaul@chromium.org>
>>>> ---
>>>> Changes in v2:
>>>> - update patch following Enric suggest
>>>> Changes in v3:
>>>> - use variable fw_training instead sw_training_success
>>>> - base on DP SPCE, if training fail use lower link rate to retry training
>>>> Changes in v4:
>>>> - improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link()
>>>> follow Sean suggest
>>>> Changes in v5:
>>>> - fix some whitespcae issue
>>>> Changes in v6:
>>>> - None
>>>>
>>>>   drivers/gpu/drm/rockchip/Makefile               |   3 +-
>>>>   drivers/gpu/drm/rockchip/cdn-dp-core.c          |  24 +-
>>>>   drivers/gpu/drm/rockchip/cdn-dp-core.h          |   2 +
>>>>   drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420
>>>> ++++++++++++++++++++++++
>>>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c           |  31 +-
>>>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h           |  38 ++-
>>>>   6 files changed, 505 insertions(+), 13 deletions(-)
>>>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>>
>>>> diff --git a/drivers/gpu/drm/rockchip/Makefile
>>>> b/drivers/gpu/drm/rockchip/Makefile
>>>> index a314e21..b932f62 100644
>>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>>> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>>>
>>>>   rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>>>> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>>>> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
>>>> +                                       cdn-dp-link-training.o
>>>>   rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>>>   rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>>>>   rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>> b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>> index cce64c1..d9d0d4d 100644
>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct
>>>> drm_encoder *encoder)
>>>>                          goto out;
>>>>                  }
>>>>          }
>>>> -
>>>> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>>>> -       if (ret) {
>>>> -               DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
>>>> -               goto out;
>>>> +       if (dp->use_fw_training == true) {
>>>
>>> You don't need to compare to true. Simply do:
>>>
>>> if (dp->use_fw_training)
>>>
>>>> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>>>> +               if (ret) {
>>>> +                       DRM_DEV_ERROR(dp->dev,
>>>> +                                     "Failed to idle video %d\n", ret);
>>>> +                       goto out;
>>>> +               }
>>>>          }
>>>>
>>>>          ret = cdn_dp_config_video(dp);
>>>> @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct
>>>> drm_encoder *encoder)
>>>>                  goto out;
>>>>          }
>>>>
>>>> -       ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>>>> -       if (ret) {
>>>> -               DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n",
>>>> ret);
>>>> -               goto out;
>>>> +       if (dp->use_fw_training == true) {
>>>
>>> if (dp->use_fw_training)
>>>
>>>> +               ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>>>> +               if (ret) {
>>>> +                       DRM_DEV_ERROR(dp->dev,
>>>> +                               "Failed to valid video %d\n", ret);
>>>> +                       goto out;
>>>> +               }
>>>>          }
>>>> +
>>>>   out:
>>>>          mutex_unlock(&dp->lock);
>>>>   }
>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>> b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>> index 46159b2..77a9793 100644
>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>>>>          bool connected;
>>>>          bool active;
>>>>          bool suspended;
>>>> +       bool use_fw_training;
>>>>
>>>>          const struct firmware *fw;      /* cdn dp firmware */
>>>>          unsigned int fw_version;        /* cdn fw version */
>>>> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>>>>          u8 ports;
>>>>          u8 lanes;
>>>>          int active_port;
>>>> +       u8 train_set[4];
>>>>
>>>>          u8 dpcd[DP_RECEIVER_CAP_SIZE];
>>>>          bool sink_has_audio;
>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>> b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>> new file mode 100644
>>>> index 0000000..73c3290
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>> @@ -0,0 +1,420 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>>> + * Author: Chris Zhong <zyw@rock-chips.com>
>>>> + */

Oh, I just noticed that this is not the preferred format for .c files,
see the discussion here

https://lkml.org/lkml/2017/11/25/133

>>>> +
>>>> +#include <linux/device.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/phy/phy.h>
>>>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>>> +
>>>> +#include "cdn-dp-core.h"
>>>> +#include "cdn-dp-reg.h"
>>>> +
>>>> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
>>>> +{
>>>> +       struct cdn_dp_port *port = dp->port[dp->active_port];
>>>> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>>>> +
>>>> +       int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>>>> +       u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
>>>> +                  DP_TRAIN_VOLTAGE_SWING_SHIFT;
>>>> +       u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>>>> +                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
>>>> +
>>>> +       tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
>>>> +                               swing, pre_emphasis);
>>>> +}
>>>> +
>>>> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t
>>>> dp_train_pat)
>>>> +{
>>>> +       u32 phy_config, global_config;
>>>> +       int ret;
>>>> +       uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
>>>> +
>>>> +       global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
>>>> +                       GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
>>>> +
>>>> +       phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
>>>> +                    DP_TX_PHY_SKEW_BYPASS(0) |
>>>> +                    DP_TX_PHY_DISPARITY_RST(0) |
>>>> +                    DP_TX_PHY_LANE0_SKEW(0) |
>>>> +                    DP_TX_PHY_LANE1_SKEW(1) |
>>>> +                    DP_TX_PHY_LANE2_SKEW(2) |
>>>> +                    DP_TX_PHY_LANE3_SKEW(3) |
>>>> +                    DP_TX_PHY_10BIT_ENABLE(0);
>>>> +
>>>> +       if (pattern != DP_TRAINING_PATTERN_DISABLE) {
>>>> +               global_config |= NO_VIDEO;
>>>> +               phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
>>>> +                             DP_TX_PHY_SCRAMBLER_BYPASS(1) |
>>>> +                             DP_TX_PHY_TRAINING_PATTERN(pattern);
>>>> +       }
>>>> +
>>>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG,
>>>> global_config);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error:
>>>> %d\n",
>>>> +                         ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error:
>>>> %d\n",
>>>> +                         ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes)
>>>> - 1);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       if (drm_dp_enhanced_frame_cap(dp->dpcd))
>>>> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
>>>> +       else
>>>> +               ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
>>>> +       if (ret)
>>>> +               DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
>>>> +
>>>> +       return ret;
>>>> +}
>>>> +
>>>> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
>>>> +{
>>>> +       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> +               return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> +               return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +       case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> +               return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> +       default:
>>>> +               return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +       }
>>>> +}
>>>> +
>>>> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
>>>> +                                   uint8_t
>>>> link_status[DP_LINK_STATUS_SIZE])
>>>> +{
>>>> +       int i;
>>>> +       uint8_t v = 0, p = 0;
>>>> +       uint8_t preemph_max;
>>>> +
>>>> +       for (i = 0; i < dp->link.num_lanes; i++) {
>>>> +               v = max(v, drm_dp_get_adjust_request_voltage(link_status,
>>>> i));
>>>> +               p = max(p,
>>>> drm_dp_get_adjust_request_pre_emphasis(link_status,
>>>> +                                                                 i));
>>>> +       }
>>>> +
>>>> +       if (v >= VOLTAGE_LEVEL_2)
>>>> +               v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
>>>> +
>>>> +       preemph_max = cdn_dp_pre_emphasis_max(v);
>>>> +       if (p >= preemph_max)
>>>> +               p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
>>>> +
>>>> +       for (i = 0; i < dp->link.num_lanes; i++)
>>>> +               dp->train_set[i] = v | p;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Pick training pattern for channel equalization. Training Pattern 3
>>>> for HBR2
>>>> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
>>>> + */
>>>> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
>>>> +{
>>>> +       u32 training_pattern = DP_TRAINING_PATTERN_2;
>>>> +
>>>> +       /*
>>>> +        * cdn dp support HBR2 also support TPS3. TPS3 support is also
>>>> mandatory
>>>> +        * for downstream devices that support HBR2. However, not all
>>>> sinks
>>>> +        * follow the spec.
>>>> +        */
>>>> +       if (drm_dp_tps3_supported(dp->dpcd))
>>>> +               training_pattern = DP_TRAINING_PATTERN_3;
>>>> +       else
>>>> +               DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3
>>>> support\n");
>>>> +
>>>> +       return training_pattern;
>>>> +}
>>>> +
>>>> +
>>>> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
>>>> +{
>>>> +       int lane;
>>>> +
>>>> +       for (lane = 0; lane < dp->link.num_lanes; lane++)
>>>> +               if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) ==
>>>> 0)
>>>> +                       return false;
>>>> +
>>>> +       return true;
>>>> +}
>>>> +
>>>> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       cdn_dp_set_signal_levels(dp);
>>>> +
>>>> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
>>>> +                               dp->train_set, dp->link.num_lanes);
>>>> +       if (ret != dp->link.num_lanes)
>>>> +               return -EINVAL;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
>>>> +                                 uint8_t dp_train_pat)
>>>> +{
>>>> +       uint8_t buf[sizeof(dp->train_set) + 1];
>>>> +       int ret, len;
>>>> +
>>>> +       buf[0] = dp_train_pat;
>>>> +       if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
>>>> +           DP_TRAINING_PATTERN_DISABLE) {
>>>> +               /* don't write DP_TRAINING_LANEx_SET on disable */
>>>> +               len = 1;
>>>> +       } else {
>>>> +               /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET
>>>> */
>>>> +               memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
>>>> +               len = dp->link.num_lanes + 1;
>>>> +       }
>>>> +
>>>> +       ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
>>>> +                               buf, len);
>>>> +       if (ret != len)
>>>> +               return -EINVAL;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
>>>> +                                   uint8_t dp_train_pat)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       memset(dp->train_set, 0, sizeof(dp->train_set));
>>>> +
>>>> +       cdn_dp_set_signal_levels(dp);
>>>> +
>>>> +       ret = cdn_dp_set_pattern(dp, dp_train_pat);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       return cdn_dp_set_link_train(dp, dp_train_pat);
>>>> +}
>>>> +
>>>> +/* Enable corresponding port and start training pattern 1 */
>>>> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
>>>> +{
>>>> +       u8 voltage;
>>>> +       u8 link_status[DP_LINK_STATUS_SIZE];
>>>> +       u32 voltage_tries, max_vswing_tries;
>>>> +       int ret;
>>>> +
>>>> +       /* clock recovery */
>>>> +       ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
>>>> +                                         DP_LINK_SCRAMBLING_DISABLE);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("failed to start link train\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       voltage_tries = 1;
>>>> +       max_vswing_tries = 0;
>>>> +       for (;;) {
>>>> +               drm_dp_link_train_clock_recovery_delay(dp->dpcd);
>>>> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status)
>>>> !=
>>>> +                   DP_LINK_STATUS_SIZE) {
>>>> +                       DRM_ERROR("failed to get link status\n");
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +
>>>> +               if (drm_dp_clock_recovery_ok(link_status,
>>>> dp->link.num_lanes)) {
>>>> +                       DRM_DEBUG_KMS("clock recovery OK\n");
>>>> +                       return 0;
>>>> +               }
>>>> +
>>>> +               if (voltage_tries >= 5) {
>>>> +                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +
>>>> +               if (max_vswing_tries >= 1) {
>>>> +                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +
>>>> +               voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
>>>> +
>>>> +               /* Update training set as requested by target */
>>>> +               cdn_dp_get_adjust_train(dp, link_status);
>>>> +               if (cdn_dp_update_link_train(dp)) {
>>>> +                       DRM_ERROR("failed to update link training\n");
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +
>>>> +               if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
>>>> +                   voltage)
>>>> +                       ++voltage_tries;
>>>> +               else
>>>> +                       voltage_tries = 1;
>>>> +
>>>> +               if (cdn_dp_link_max_vswing_reached(dp))
>>>> +                       ++max_vswing_tries;
>>>> +       }
>>>> +}
>>>> +
>>>> +static int cdn_dp_link_training_channel_equalization(struct
>>>> cdn_dp_device *dp)
>>>> +{
>>>> +       int tries, ret;
>>>> +       u32 training_pattern;
>>>> +       uint8_t link_status[DP_LINK_STATUS_SIZE];
>>>> +
>>>> +       training_pattern = cdn_dp_select_chaneq_pattern(dp);
>>>> +       training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
>>>> +
>>>> +       ret = cdn_dp_set_pattern(dp, training_pattern);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       ret = cdn_dp_set_link_train(dp, training_pattern);
>>>> +       if (ret) {
>>>> +               DRM_ERROR("failed to start channel equalization\n");
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       for (tries = 0; tries < 5; tries++) {
>>>> +               drm_dp_link_train_channel_eq_delay(dp->dpcd);
>>>> +               if (drm_dp_dpcd_read_link_status(&dp->aux, link_status)
>>>> !=
>>>> +                   DP_LINK_STATUS_SIZE) {
>>>> +                       DRM_ERROR("failed to get link status\n");
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               /* Make sure clock is still ok */
>>>> +               if (!drm_dp_clock_recovery_ok(link_status,
>>>> +                                             dp->link.num_lanes)) {
>>>> +                       DRM_DEBUG_KMS("Clock recovery check failed\n");
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               if (drm_dp_channel_eq_ok(link_status,
>>>> dp->link.num_lanes)) {
>>>> +                       DRM_DEBUG_KMS("Channel EQ done\n");
>>>> +                       return 0;
>>>> +               }
>>>> +
>>>> +               /* Update training set as requested by target */
>>>> +               cdn_dp_get_adjust_train(dp, link_status);
>>>> +               if (cdn_dp_update_link_train(dp)) {
>>>> +                       DRM_ERROR("failed to update link training\n");
>>>> +                       break;
>>>> +               }
>>>> +       }
>>>> +
>>>> +       /* Try 5 times, else fail and try at lower BW */
>>>> +       if (tries == 5)
>>>> +               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
>>>> +
>>>> +       return -EINVAL;
>>>> +}
>>>> +
>>>> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
>>>> +{
>>>> +       int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
>>>> +
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
>>>> +}
>>>> +
>>>> +static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp)
>>>> +{
>>>> +       switch (dp->link.rate) {
>>>> +       case DP_LINK_BW_1_62:
>>>> +               return -EINVAL;
>>>> +       case DP_LINK_BW_2_7:
>>>> +               dp->link.rate = DP_LINK_BW_1_62;
>>>> +               break;
>>>> +       case DP_LINK_BW_5_4:
>>>> +               dp->link.rate = DP_LINK_BW_2_7;
>>>> +               break;
>>>> +       default:
>>>> +               dp->link.rate = DP_LINK_BW_5_4;
>>>> +               break;
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
>>>> +{
>>>> +       int ret, stop_err;
>>>> +       u8 link_config[2];
>>>> +       u32 rate, sink_max, source_max;
>>>> +
>>>> +       ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
>>>> +                              sizeof(dp->dpcd));
>>>> +       if (ret < 0) {
>>>> +               DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>>>> +               return ret;
>>>> +       }
>>>> +
>>>> +       source_max = dp->lanes;
>>>> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
>>>> +       dp->link.num_lanes = min(source_max, sink_max);
>>>> +
>>>> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
>>>> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
>>>> +       rate = min(source_max, sink_max);
>>>> +       dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
>>>> +
>>>> +       link_config[0] = 0;
>>>> +       link_config[1] = 0;
>>>> +       if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
>>>> +               link_config[1] = DP_SET_ANSI_8B10B;
>>>> +       drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
>>>> +
>>>> +       while (true) {
>>>> +
>>>> +               /* Write the link configuration data */
>>>> +               link_config[0] = dp->link.rate;
>>>> +               link_config[1] = dp->link.num_lanes;
>>>> +               if (drm_dp_enhanced_frame_cap(dp->dpcd))
>>>> +                       link_config[1] |=
>>>> DP_LANE_COUNT_ENHANCED_FRAME_EN;
>>>> +               drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config,
>>>> 2);
>>>> +
>>>> +               ret = cdn_dp_link_training_clock_recovery(dp);
>>>> +               if (ret) {
>>>> +                       if (!cdn_dp_get_lower_link_rate(dp))
>>>> +                               continue;
>>>> +
>>>> +                       DRM_ERROR("training clock recovery failed: %d\n",
>>>> ret);
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               ret = cdn_dp_link_training_channel_equalization(dp);
>>>> +               if (ret) {
>>>> +                       if (!cdn_dp_get_lower_link_rate(dp))
>>>> +                               continue;
>>>> +
>>>> +                       DRM_ERROR("training channel eq failed: %d\n",
>>>> ret);
>>>> +                       break;
>>>> +               }
>>>> +
>>>> +               break;
>>>> +       }
>>>> +
>>>> +       stop_err = cdn_dp_stop_link_train(dp);
>>>> +       if (stop_err) {
>>>> +               DRM_ERROR("stop training fail, error: %d\n", stop_err);
>>>> +               return stop_err;
>>>> +       }
>>>> +
>>>> +       return ret;
>>>> +}
>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>> b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>> index 979355d..e1273e6 100644
>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>> @@ -17,7 +17,9 @@
>>>>   #include <linux/delay.h>
>>>>   #include <linux/io.h>
>>>>   #include <linux/iopoll.h>
>>>> +#include <linux/phy/phy.h>
>>>>   #include <linux/reset.h>
>>>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>>>
>>>>   #include "cdn-dp-core.h"
>>>>   #include "cdn-dp-reg.h"
>>>> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device
>>>> *dp, u8 module_id,
>>>>          return 0;
>>>>   }
>>>>
>>>> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>>>   {
>>>>          u8 msg[6];
>>>>
>>>> @@ -609,6 +611,31 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
>>>>   {
>>>>          int ret;
>>>>
>>>> +       /*
>>>> +        * DP firmware uses fixed phy config values to do training, but
>>>> some
>>>> +        * boards need to adjust these values to fit for their unique
>>>> hardware
>>>> +        * design. So if the phy is using custom config values, do
>>>> software
>>>> +        * link training instead of relying on firmware, if software
>>>> training
>>>> +        * fail, keep firmware training as a fallback if sw training
>>>> fails.
>>>> +        */
>>>> +       ret = cdn_dp_software_train_link(dp);
>>>> +       if (ret) {
>>>> +               DRM_DEV_ERROR(dp->dev,
>>>> +                       "Failed to do software training %d\n", ret);
>>>> +               goto do_fw_training;
>>>> +       }
>>>
>>> If I understand correctly you are changing current behavior. Before
>>> this patch, we use always firmware link training, and after this
>>> patch, we always use software link training. If fails we use the
>>> firmware link training.
>>>
>>> AFAIK my Samsung Chromebook Plus works well with firmware link
>>> training, so there are any benefits of use software link training
>>> instead of firmware link training?
>>>
>>> Looks to me that we should only do software link training on these
>>> platforms that need it, so on those that define the phy-config
>>> property in their DT and use by default firmware link training.
>>
>> Sean and me have discussed about that, and we all agree to use sw training
>> instead the
>> fw training to keep training process consistent, and we do not need add a
>> varialbe in
>> struct rockchip_typec_phy(like use_sw_training before) to distinguish sw and
>> fw training.
>> If training process implement correctly, sw training not different with fw
>> training, and as my
>> test so far, sw training work well on Kevin, ofcourse, we need keep testing
>> it.
>>
>
> Ok, I can also confirm that software training works well on kevin.
> Could you mention this change of behavior in the commit message? I
> think that after these patches the firmware training is unlikely to
> happen.
>
> Thanks,
>  Enric
>
>
>>>> +       ret = cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
>>>> +       if (ret) {
>>>> +               DRM_DEV_ERROR(dp->dev,
>>>> +               "Failed to write SOURCE_HDTX_CAR register %d\n", ret);
>>>> +               goto do_fw_training;
>>>> +       }
>>>> +       dp->use_fw_training = false;
>>>> +       return 0;
>>>> +
>>>> +do_fw_training:
>>>> +       dp->use_fw_training = true;
>>>> +       DRM_DEV_DEBUG_KMS(dp->dev, "use fw training\n");
>>>>          ret = cdn_dp_training_start(dp);
>>>>          if (ret) {
>>>>                  DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n",
>>>> ret);
>>>> @@ -623,7 +650,7 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
>>>>
>>>>          DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n",
>>>> dp->link.rate,
>>>>                            dp->link.num_lanes);
>>>> -       return ret;
>>>> +       return 0;
>>>>   }
>>>>
>>>>   int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>> b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>> index 6580b11..3420771 100644
>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>> @@ -137,7 +137,7 @@
>>>>   #define HPD_EVENT_MASK                 0x211c
>>>>   #define HPD_EVENT_DET                  0x2120
>>>>
>>>> -/* dpyx framer addr */
>>>> +/* dptx framer addr */
>>>>   #define DP_FRAMER_GLOBAL_CONFIG                0x2200
>>>>   #define DP_SW_RESET                    0x2204
>>>>   #define DP_FRAMER_TU                   0x2208
>>>> @@ -431,6 +431,40 @@
>>>>   /* Reference cycles when using lane clock as reference */
>>>>   #define LANE_REF_CYC                           0x8000
>>>>
>>>> +/* register CM_VID_CTRL */
>>>> +#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) <<
>>>> 0)
>>>> +#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) <<
>>>> 24)
>>>> +
>>>> +/* register DP_TX_PHY_CONFIG_REG */
>>>> +#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
>>>> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
>>>> +#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
>>>> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
>>>> +#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
>>>> +#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
>>>> +#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
>>>> +#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
>>>> +#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
>>>> +#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
>>>> +#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
>>>> +#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
>>>> +
>>>> +/* register DP_FRAMER_GLOBAL_CONFIG */
>>>> +#define NUM_LANES(x)           ((x) & 3)
>>>> +#define SST_MODE               (0 << 2)
>>>> +#define RG_EN                  (0 << 4)
>>>> +#define GLOBAL_EN              BIT(3)
>>>> +#define NO_VIDEO               BIT(5)
>>>> +#define ENC_RST_DIS            BIT(6)
>>>> +#define WR_VHSYNC_FALL         BIT(7)
>>>> +
>>>>   enum voltage_swing_level {
>>>>          VOLTAGE_LEVEL_0,
>>>>          VOLTAGE_LEVEL_1,
>>>> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8
>>>> lanes, bool flip);
>>>>   int cdn_dp_event_config(struct cdn_dp_device *dp);
>>>>   u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>>>>   int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>>>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>>>>   ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>>>>                            u8 *data, u16 len);
>>>>   ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
>>>> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>>>>   int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info
>>>> *audio);
>>>>   int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>>>>   int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info
>>>> *audio);
>>>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>>>>   #endif /* _CDN_DP_REG_H */
>>>> --
>>>> 2.7.4
>>>>
>>>
>>>
>>
>>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* [PATCH] arm64: dts: specify 1.8V EMMC capabilities for bcm958742t
From: Scott Branden @ 2018-05-22 17:01 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Catalin Marinas, Will Deacon,
	Florian Fainelli
  Cc: BCM Kernel Feedback, devicetree, linux-arm-kernel, linux-kernel,
	Scott Branden

Specify 1.8V EMMC capabilities for bcm958742t board to indicate support
for UHS mode.

Fixes: d4b4aba6be8a ("arm64: dts: Initial DTS files for Broadcom Stingray SOC")
Signed-off-by: Scott Branden <scott.branden@broadcom.com>
---
 arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts b/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts
index 6a4d19e..dd9de6b 100644
--- a/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts
+++ b/arch/arm64/boot/dts/broadcom/stingray/bcm958742t.dts
@@ -44,3 +44,7 @@
 &gphy0 {
 	enet-phy-lane-swap;
 };
+
+&sdio0 {
+	mmc-ddr-1_8v;
+};
-- 
2.5.0

^ permalink raw reply related

* Re: [PATCH v3 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings
From: Mark Brown @ 2018-05-22 16:55 UTC (permalink / raw)
  To: Doug Anderson
  Cc: David Collins, Liam Girdwood, Rob Herring, Mark Rutland,
	linux-arm-msm, Linux ARM, devicetree, LKML, Rajendra Nayak,
	Stephen Boyd
In-Reply-To: <CAD=FV=XARtQNo5Cyg78XuT26JUFgn=7BjWRnXPjP566=a-sF1w@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 4283 bytes --]

On Tue, May 22, 2018 at 09:43:02AM -0700, Doug Anderson wrote:
> On Mon, May 21, 2018 at 5:00 PM, David Collins <collinsd@codeaurora.org> wrote:

> > Returning the cached (but not sent) initial voltage equal to the min
> > constraint voltage in get_voltage() calls should not cause any problems.
> > This represents the highest voltage that is guaranteed to be output by the
> > regulator.  Consumer's should call regulator_set_voltage() to specify
> > their voltage needs.  If they simply call regulator_enable(), then the
> > cached voltage will be sent along with the enable request.

> I'm still not seeing the argument for initial-voltage here.  If we
> added a feature like you describe where we don't send the voltage
> until the first enable couldn't we use the minimum voltage here?  If a
> consumer calls regulator_enable() without setting a voltage (which
> seems like a terrible idea for something where the voltage could vary)
> then it would end up at the minimum voltage.

Or if something else has already set a voltage...  though shared voltage
varying regulators aren't a superb idea they do occasionally happen.

> >> I was asking for specific examples.  Do you have any?

> > I was able to track down an example that requires initialization at a
> > non-minimum voltage: PM8998 LDO 13 on SDM845 MTP.  This regulator supplies
> > the microSD card with a voltage in the range 1.8 V to 2.96 V.  The boot
> > loader leaves this regulator enabled at 2.96 V.  It is only guaranteed to
> > be safe to reduce the voltage to 1.8 V on UHS type cards and only after
> > following the proper SD identification and command sequence.

> Ironically, this is also a perfect example of why we _shouldn't_ have
> an "initial-voltage" specified in the device tree.  It is certainly
> plausible to imagine a bootloader that might enable UHS speeds on an
> SD card and leave the rail on at 1.8V.  Having an initial-voltage
> specified in the device tree would be a bad idea here because it's
> even worse to transition to 2.96V when the card was expecting 1.8V.

Right, this sort of situation is why the regulator API has a policy of
leaving things untouched unless it was specifically told to do
something.

> I suppose this is a theoretical example since (as far as I know) no
> bootloaders run the micro SD card at UHS speeds, but it is still

kexec is the most obvious example I can think of here.  You could
probably arrange for something to patch the device tree that's provided
to the kexeced kernel to tell it about the current state but we don't
currently do anything there.

> OK, so how's this for a proposal then:

> 1. For RPMh-regulator whenever we see a "set voltage" but Linux hasn't
> specified that the regulator is enabled then we don't send the
> voltage, we just cache it.

> 2. When we see the first enable then we first send the cached voltage
> and then do the enable.

> 3. We don't need an "initial voltage" because any rails that are
> expected to be variable voltage the client should be choosing a
> voltage.

That seems relatively safe.

> You can even come up with a less contrived use case here.  One could
> argue that the SD card framework really ought to be ensuring VMMC and
> VQMMC are off before it starts tweaking with them just in case the
> bootloader left them on.  Thus, it should do:

> A) Turn off VMMC and VQMMC
> B) Program VMMC and VQMMC to defaults
> C) Turn on VMMC and VQMMC

> ...right now we bootup and pretend to Linux that VMMC and VQMMC start
> off, so step A) will be no-op.  Sigh.

> Do we need to have ".is_enabled" return -ENOTRECOVERABLE to help the
> regulator framework understand that we don't know the state?  I think

Yes, we should be doing that.  Then subsystems where it's an issue can
explicitly turn off the supply.

> that might require a pile of patches to the regulator framework, but
> it can't be helped unless we can somehow get RPMh to give us back the
> status of how the bootloader left us (if we had that, we could return
> 0 for anything the bootloader didn't touch and that would be correct
> from the point of view of the AP).

I think it's fine from a framework point of view, just provide an
is_enabled() operation which returns the error.  The required fiddling
in the core should be fairly minor.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
From: Jacob Pan @ 2018-05-22 16:43 UTC (permalink / raw)
  To: Jean-Philippe Brucker
  Cc: kvm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-pci-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	xuzaibo-hv44wF8Li93QT0dZR+AlfA@public.gmane.org, Will Deacon,
	okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org,
	linux-mm-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org,
	ashok.raj-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org,
	bharatku-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	rfranz-YGCgFSpz5w/QT0dZR+AlfA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	rgummal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org,
	ilias.apalodimas-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org,
	christian.koenig-5C7GfCeVMHo@public.gmane.org
In-Reply-To: <de478769-9f7a-d40b-a55e-e2c63ad883e8-5wv7dgnIgG8@public.gmane.org>

On Thu, 17 May 2018 11:02:42 +0100
Jean-Philippe Brucker <jean-philippe.brucker-5wv7dgnIgG8@public.gmane.org> wrote:

> On 17/05/18 00:31, Jacob Pan wrote:
> > On Fri, 11 May 2018 20:06:04 +0100
> > I am a little confused about domain vs. pasid relationship. If
> > each domain represents a address space, should there be a domain for
> > each pasid?  
> 
> I don't think there is a formal definition, but from previous
> discussion the consensus seems to be: domains are a collection of
> devices that have the same virtual address spaces (one or many).
> 
> Keeping that definition makes things easier, in my opinion. Some time
> ago, I did try to represent PASIDs using "subdomains" (introducing a
> hierarchy of struct iommu_domain), but it required invasive changes in
> the IOMMU subsystem and probably all over the tree.
> 
> You do need some kind of "root domain" for each device, so that
> "iommu_get_domain_for_dev()" still makes sense. That root domain
> doesn't have a single address space but a collection of subdomains.
> If you need this anyway, representing a PASID with an iommu_domain
> doesn't seem preferable than using a different structure (io_mm),
> because they don't have anything in common.
> 
My main concern is the PASID table storage. If PASID table storage
is tied to domain, it is ok to scale up, i.e. multiple devices in a
domain share a single PASID table. But to scale down, e.g. further
partition device with VFIO mdev for assignment, each mdev may get its
own domain via vfio. But there is no IOMMU storage for PASID table at
mdev device level. Perhaps we do need root domain or some parent-child
relationship to locate PASID table.

> Thanks,
> Jean

[Jacob Pan]

^ permalink raw reply

* Re: [PATCH v3 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings
From: Doug Anderson @ 2018-05-22 16:43 UTC (permalink / raw)
  To: David Collins
  Cc: Mark Rutland, devicetree, Rajendra Nayak, Stephen Boyd,
	linux-arm-msm, Liam Girdwood, Rob Herring, LKML, Mark Brown,
	Linux ARM
In-Reply-To: <fb390ccb-927a-8449-05c3-8dcac964bfae@codeaurora.org>

Hi,

On Mon, May 21, 2018 at 5:00 PM, David Collins <collinsd@codeaurora.org> wrote:
> On 05/21/2018 11:01 AM, Doug Anderson wrote:
>> On Fri, May 18, 2018 at 5:46 PM, David Collins <collinsd@codeaurora.org> wrote:
> ...
>>> Something to keep in mind about the downstream rpmh-regulator driver is
>>> that it caches the initial voltages specified in device tree and only
>>> sends them after a consumer driver makes a regulator framework call. This
>>> saves time during boot and ensures that requests are not made for
>>> regulators that no Linux consumer cares about.
>>
>> You're saying that in the downstream driver you'd specify
>> "initial-voltage" in the device tree and:
>>
>> * This voltage would be reported by any subsequent get_voltage() calls
>>
>> * This voltage would _not_ be communicated to RPMh.
>>
>> That seems really strange because you're essentially going to be
>> returning something from get_voltage() that could be a lie.  You don't
>> know if the BIOS actually set the value or not but you'll claim that
>> it did.  It also doesn't seem to match what I see in the downstream
>> driver.  There I see it read "qcom,init-voltage" and then do a
>> "rpmh_regulator_set_reg()".  Thus my reading of the downstream driver
>> is that it should do the same requests that you're doing.
>
> In the downstream rpmh-regulator driver [1], the value specified via the
> "qcom,init-voltage" DT property is only sent to RPMh at probe time if the
> "qcom,send-defaults" property is also specified.  "qcom,send-defaults" is
> currently specified only for PMI8998 BOB.  The rpmh_regulator_set_reg()
> function only updates the cached RPMh request value to be sent in the next
> request.  The rpmh_regulator_send_aggregate_requests() function must be
> called to actually issue the request.

Got it.  This wasn't in the "snapshot" of the downstream driver that I saw.


> Returning the cached (but not sent) initial voltage equal to the min
> constraint voltage in get_voltage() calls should not cause any problems.
> This represents the highest voltage that is guaranteed to be output by the
> regulator.  Consumer's should call regulator_set_voltage() to specify
> their voltage needs.  If they simply call regulator_enable(), then the
> cached voltage will be sent along with the enable request.

I'm still not seeing the argument for initial-voltage here.  If we
added a feature like you describe where we don't send the voltage
until the first enable couldn't we use the minimum voltage here?  If a
consumer calls regulator_enable() without setting a voltage (which
seems like a terrible idea for something where the voltage could vary)
then it would end up at the minimum voltage.


>>> It is generally not safe to request all regulators to be set to the
>>> minimum allowed voltage.  Special care will be needed with the upstream
>>> qcom-rpmh-regulator driver to avoid disrupting the boot up state of
>>> regulators that are needed by other subsystems.  Therefore, I would like
>>> to keep the initial voltage feature supported.
>>
>> I was asking for specific examples.  Do you have any?
>
> I was able to track down an example that requires initialization at a
> non-minimum voltage: PM8998 LDO 13 on SDM845 MTP.  This regulator supplies
> the microSD card with a voltage in the range 1.8 V to 2.96 V.  The boot
> loader leaves this regulator enabled at 2.96 V.  It is only guaranteed to
> be safe to reduce the voltage to 1.8 V on UHS type cards and only after
> following the proper SD identification and command sequence.

Ironically, this is also a perfect example of why we _shouldn't_ have
an "initial-voltage" specified in the device tree.  It is certainly
plausible to imagine a bootloader that might enable UHS speeds on an
SD card and leave the rail on at 1.8V.  Having an initial-voltage
specified in the device tree would be a bad idea here because it's
even worse to transition to 2.96V when the card was expecting 1.8V.

I suppose this is a theoretical example since (as far as I know) no
bootloaders run the micro SD card at UHS speeds, but it is still
worrying that the kernel needs to have a hardcoded initial voltage in
it.  ...basically whenever we're playing with the voltage at bootup
before a regulator has been claimed it's not amazingly safe.
Generally Linux doesn't need to worry about this because it will only
play with voltages if they are out of the constrained range, but in
Qualcomm's case where we can't read the existing voltages then we get
badness.


>> BTW: have I already said how terrible of a design it is that you can't
>> read back the voltages that the BIOS set?  If we could just read back
>> what the BIOS set then everything would work great and we could stop
>> talking about this.
>
> Even if such reading were feasible, it would not help the situation
> substantially.  Very few requests are made via the AP RSC before Linux
> kernel boot, so 0 V values would still be read back for most regulators.

Sure, but all the regulators we're talking about are ones where this
would help.  Said another way: are there any rails that are not
touched by the bootloader where it's bad to set the minimum voltage?


OK, so how's this for a proposal then:

1. For RPMh-regulator whenever we see a "set voltage" but Linux hasn't
specified that the regulator is enabled then we don't send the
voltage, we just cache it.

2. When we see the first enable then we first send the cached voltage
and then do the enable.

3. We don't need an "initial voltage" because any rails that are
expected to be variable voltage the client should be choosing a
voltage.


...taking the SD card case as an example: if the regulator framework
says "set to the minimum: 1.8V" we'll cache this but not apply it yet
because the rail is off as far as Linux is concerned.  Then when the
SD card framework starts up it will set the voltage to 3.3V which will
overwrite the cache.  Then the SD card framework will enable the
regulator and RPMh will set the voltage to 3.3V and enable.


This whole thing makes me worry about another problem, though.  You
say that the bootloader left the SD card rail on, right?  ...but as
far as Linux is concerned the SD card rail is off (argh, it can't read
the state that the bootloader left the rail in!)

...now imagine any of the following:

* The user boots up a kernel where the SD card driver is disabled.

* The user ejects the SD card right after the bootloader used it but
before the kernel driver started.

When the kernel comes up it will believe that the SD card rail is
disabled so it won't try to disable it.  So the voltage will be left
on.


You can even come up with a less contrived use case here.  One could
argue that the SD card framework really ought to be ensuring VMMC and
VQMMC are off before it starts tweaking with them just in case the
bootloader left them on.  Thus, it should do:

A) Turn off VMMC and VQMMC
B) Program VMMC and VQMMC to defaults
C) Turn on VMMC and VQMMC

...right now we bootup and pretend to Linux that VMMC and VQMMC start
off, so step A) will be no-op.  Sigh.


Do we need to have ".is_enabled" return -ENOTRECOVERABLE to help the
regulator framework understand that we don't know the state?  I think
that might require a pile of patches to the regulator framework, but
it can't be helped unless we can somehow get RPMh to give us back the
status of how the bootloader left us (if we had that, we could return
0 for anything the bootloader didn't touch and that would be correct
from the point of view of the AP).


-Doug

^ permalink raw reply

* Re: [v4 07/11] dt-bindings: hwmon: Add documents for PECI hwmon client drivers
From: Rob Herring @ 2018-05-22 16:42 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Haiyue Wang, Vernon Mauery, James Feist, devicetree,
	linux-kernel, Andrew Jeffery, Arnd Bergmann, Jason M Biils,
	Joel Stanley
In-Reply-To: <20180521195905.27983-1-jae.hyun.yoo@linux.intel.com>

On Mon, May 21, 2018 at 12:59:05PM -0700, Jae Hyun Yoo wrote:
> This commit adds dt-bindings documents for PECI hwmon client drivers.
> 
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> Reviewed-by: James Feist <james.feist@linux.intel.com>
> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> ---
>  .../bindings/hwmon/peci-cputemp.txt           | 23 ++++++++++++++++++
>  .../bindings/hwmon/peci-dimmtemp.txt          | 24 +++++++++++++++++++
>  2 files changed, 47 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/hwmon/peci-cputemp.txt
>  create mode 100644 Documentation/devicetree/bindings/hwmon/peci-dimmtemp.txt
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/peci-cputemp.txt b/Documentation/devicetree/bindings/hwmon/peci-cputemp.txt
> new file mode 100644
> index 000000000000..2f59aee12d9e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/peci-cputemp.txt
> @@ -0,0 +1,23 @@
> +Bindings for Intel PECI (Platform Environment Control Interface) cputemp driver.
> +
> +Required properties:
> +- compatible : Should be "intel,peci-cputemp".
> +- reg        : Should contain address of a client CPU. Address range of CPU
> +	       clients is starting from 0x30 based on PECI specification.
> +
> +Example:
> +	peci-bus@0 {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		< more properties >
> +
> +		peci-cputemp@30 {
> +			compatible = "intel,peci-cputemp";
> +			reg = <0x30>;
> +		};

[...]

> +		peci-dimmtemp@30 {
> +			compatible = "intel,peci-dimmtemp";
> +			reg = <0x30>;
> +		};

As I said in the prior version, 2 nodes at the same address is wrong.

Rob

^ permalink raw reply

* Re: [PATCH 1/2] regulator: add QCOM RPMh regulator driver
From: Mark Brown @ 2018-05-22 16:36 UTC (permalink / raw)
  To: David Collins
  Cc: Stephen Boyd, lgirdwood, mark.rutland, robh+dt, linux-arm-msm,
	linux-arm-kernel, devicetree, linux-kernel, rnayak, ilina
In-Reply-To: <6cb2ac52-258c-332b-e912-809d16e14114@codeaurora.org>

[-- Attachment #1: Type: text/plain, Size: 1815 bytes --]

On Thu, May 17, 2018 at 01:48:41PM -0700, David Collins wrote:

> The RPMh hardware is configured by the boot loader.  The configuration
> does reflect reality; however, it cannot handle all configurations at
> initialization time.  Specific headroom management typically comes up in
> modem usecases for RF supplies that are sensitive to noise.  This feature

...

> If you really don't like having this feature present in the Linux RPMh
> regulator driver, then I'd be ok removing it.  It is not required for
> SDM845 which the driver is initially targeting.

It's certainly going to be easier to review it separately.

> >> In the case of XOB managed LDO regulators, the LDOs physically can be
> >> configured to different voltages by the bootloader.  However, the RPMh
> >> interface provides no mechanism for the application processor to read or
> >> change that voltage.  Therefore, we need a way to specify such voltages in
> >> a board specific (as opposed to driver specific) manner (i.e. device tree).

> > Is the kernel somehow prevented from varying these voltages?

> Yes.  Physically, there exists no RPMh register to read or write the
> voltage of LDOs managed via XOB.  Additionally, the kernel running on the
> application processor is blocked from configuring the voltage via a direct
> SPMI writes by access permissions that crash the system when violated.

*sigh*  Please provide feedback on the problems this (and everything
else) is causing to the team working on the firmware.  The number of
issues with this interface compared to anything else we've seen is
really noticable, I see what it's trying to do with providing something
like the regulator API is doing but there's quite a lot of missing steps
in it which cause no end of problems for general purpose software
written on top of it.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* [PATCH v2 6/6] clk: meson: axg: add the audio clock controller driver
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel
In-Reply-To: <20180522163457.13834-1-jbrunet@baylibre.com>

The axg audio clock controller is the clock generation unit for the
amlogic audio subsystem of A113 based SoCs. It may be clocked by 8
different plls provided by the primary clock controller and also by
10 slave bit clocks and 10 slave sample clocks which may be provided
by external components, such as audio codecs, through the SoC pads.

It contains several muxes, dividers and gates which are fed into the
the different devices of the audio subsystem.

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/meson/Kconfig     |   9 +
 drivers/clk/meson/Makefile    |   1 +
 drivers/clk/meson/axg-audio.c | 845 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/meson/axg-audio.h | 127 +++++++
 4 files changed, 982 insertions(+)
 create mode 100644 drivers/clk/meson/axg-audio.c
 create mode 100644 drivers/clk/meson/axg-audio.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 9ad95966aa3d..efaa70f682b4 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -45,3 +45,12 @@ config COMMON_CLK_AXG
 	help
 	  Support for the clock controller on AmLogic A113D devices, aka axg.
 	  Say Y if you want peripherals and CPU frequency scaling to work.
+
+config COMMON_CLK_AXG_AUDIO
+	tristate "Meson AXG Audio Clock Controller Driver"
+	depends on COMMON_CLK_AXG
+	select COMMON_CLK_AMLOGIC_AUDIO
+	select MFD_SYSCON
+	help
+	  Support for the audio clock controller on AmLogic A113D devices,
+	  aka axg, Say Y if you want audio subsystem to work.
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 1574e3f0de5c..fd97f5ba317d 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
 obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
 obj-$(CONFIG_COMMON_CLK_GXBB)	 += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
 obj-$(CONFIG_COMMON_CLK_AXG)	 += axg.o axg-aoclk.o
+obj-$(CONFIG_COMMON_CLK_AXG_AUDIO)	+= axg-audio.o
 obj-$(CONFIG_COMMON_CLK_REGMAP_MESON)	+= clk-regmap.o
diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c
new file mode 100644
index 000000000000..a0ed41e73bde
--- /dev/null
+++ b/drivers/clk/meson/axg-audio.c
@@ -0,0 +1,845 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include "clkc-audio.h"
+#include "axg-audio.h"
+
+#define AXG_MST_IN_COUNT	8
+#define AXG_SLV_SCLK_COUNT	10
+#define AXG_SLV_LRCLK_COUNT	10
+
+#define AXG_AUD_GATE(_name, _reg, _bit, _pname, _iflags)		\
+struct clk_regmap axg_##_name = {					\
+	.data = &(struct clk_regmap_gate_data){				\
+		.offset = (_reg),					\
+		.bit_idx = (_bit),					\
+	},								\
+	.hw.init = &(struct clk_init_data) {				\
+		.name = "axg_"#_name,					\
+		.ops = &clk_regmap_gate_ops,				\
+		.parent_names = (const char *[]){ _pname },		\
+		.num_parents = 1,					\
+		.flags = CLK_DUTY_CYCLE_PARENT | (_iflags),		\
+	},								\
+}
+
+#define AXG_AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pnames, _iflags) \
+struct clk_regmap axg_##_name = {					\
+	.data = &(struct clk_regmap_mux_data){				\
+		.offset = (_reg),					\
+		.mask = (_mask),					\
+		.shift = (_shift),					\
+		.flags = (_dflags),					\
+	},								\
+	.hw.init = &(struct clk_init_data){				\
+		.name = "axg_"#_name,					\
+		.ops = &clk_regmap_mux_ops,				\
+		.parent_names = (_pnames),				\
+		.num_parents = ARRAY_SIZE(_pnames),			\
+		.flags = CLK_DUTY_CYCLE_PARENT | (_iflags),		\
+	},								\
+}
+
+#define AXG_AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) \
+struct clk_regmap axg_##_name = {					\
+	.data = &(struct clk_regmap_div_data){				\
+		.offset = (_reg),					\
+		.shift = (_shift),					\
+		.width = (_width),					\
+		.flags = (_dflags),					\
+	},								\
+	.hw.init = &(struct clk_init_data){				\
+		.name = "axg_"#_name,					\
+		.ops = &clk_regmap_divider_ops,				\
+		.parent_names = (const char *[]) { _pname },		\
+		.num_parents = 1,					\
+		.flags = (_iflags),					\
+	},								\
+}
+
+#define AXG_PCLK_GATE(_name, _bit)				\
+	AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0)
+
+/* Audio peripheral clocks */
+static AXG_PCLK_GATE(ddr_arb,	   0);
+static AXG_PCLK_GATE(pdm,	   1);
+static AXG_PCLK_GATE(tdmin_a,	   2);
+static AXG_PCLK_GATE(tdmin_b,	   3);
+static AXG_PCLK_GATE(tdmin_c,	   4);
+static AXG_PCLK_GATE(tdmin_lb,	   5);
+static AXG_PCLK_GATE(tdmout_a,	   6);
+static AXG_PCLK_GATE(tdmout_b,	   7);
+static AXG_PCLK_GATE(tdmout_c,	   8);
+static AXG_PCLK_GATE(frddr_a,	   9);
+static AXG_PCLK_GATE(frddr_b,	   10);
+static AXG_PCLK_GATE(frddr_c,	   11);
+static AXG_PCLK_GATE(toddr_a,	   12);
+static AXG_PCLK_GATE(toddr_b,	   13);
+static AXG_PCLK_GATE(toddr_c,	   14);
+static AXG_PCLK_GATE(loopback,	   15);
+static AXG_PCLK_GATE(spdifin,	   16);
+static AXG_PCLK_GATE(spdifout,	   17);
+static AXG_PCLK_GATE(resample,	   18);
+static AXG_PCLK_GATE(power_detect, 19);
+
+/* Audio Master Clocks */
+static const char * const mst_mux_parent_names[] = {
+	"axg_mst_in0", "axg_mst_in1", "axg_mst_in2", "axg_mst_in3",
+	"axg_mst_in4", "axg_mst_in5", "axg_mst_in6", "axg_mst_in7",
+};
+
+#define AXG_MST_MCLK_MUX(_name, _reg)					\
+	AXG_AUD_MUX(_name##_sel, _reg, 0x7, 24, CLK_MUX_ROUND_CLOSEST, \
+		    mst_mux_parent_names, CLK_SET_RATE_PARENT)
+
+static AXG_MST_MCLK_MUX(mst_a_mclk,   AUDIO_MCLK_A_CTRL);
+static AXG_MST_MCLK_MUX(mst_b_mclk,   AUDIO_MCLK_B_CTRL);
+static AXG_MST_MCLK_MUX(mst_c_mclk,   AUDIO_MCLK_C_CTRL);
+static AXG_MST_MCLK_MUX(mst_d_mclk,   AUDIO_MCLK_D_CTRL);
+static AXG_MST_MCLK_MUX(mst_e_mclk,   AUDIO_MCLK_E_CTRL);
+static AXG_MST_MCLK_MUX(mst_f_mclk,   AUDIO_MCLK_F_CTRL);
+static AXG_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
+static AXG_MST_MCLK_MUX(spdifin_clk,  AUDIO_CLK_SPDIFIN_CTRL);
+static AXG_MST_MCLK_MUX(pdm_dclk,     AUDIO_CLK_PDMIN_CTRL0);
+static AXG_MST_MCLK_MUX(pdm_sysclk,   AUDIO_CLK_PDMIN_CTRL1);
+
+#define AXG_MST_MCLK_DIV(_name, _reg)					\
+	AXG_AUD_DIV(_name##_div, _reg, 0, 16, CLK_DIVIDER_ROUND_CLOSEST, \
+		    "axg_"#_name"_sel", CLK_SET_RATE_PARENT)		\
+
+static AXG_MST_MCLK_DIV(mst_a_mclk,   AUDIO_MCLK_A_CTRL);
+static AXG_MST_MCLK_DIV(mst_b_mclk,   AUDIO_MCLK_B_CTRL);
+static AXG_MST_MCLK_DIV(mst_c_mclk,   AUDIO_MCLK_C_CTRL);
+static AXG_MST_MCLK_DIV(mst_d_mclk,   AUDIO_MCLK_D_CTRL);
+static AXG_MST_MCLK_DIV(mst_e_mclk,   AUDIO_MCLK_E_CTRL);
+static AXG_MST_MCLK_DIV(mst_f_mclk,   AUDIO_MCLK_F_CTRL);
+static AXG_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
+static AXG_MST_MCLK_DIV(spdifin_clk,  AUDIO_CLK_SPDIFIN_CTRL);
+static AXG_MST_MCLK_DIV(pdm_dclk,     AUDIO_CLK_PDMIN_CTRL0);
+static AXG_MST_MCLK_DIV(pdm_sysclk,   AUDIO_CLK_PDMIN_CTRL1);
+
+#define AXG_MST_MCLK_GATE(_name, _reg)					\
+	AXG_AUD_GATE(_name, _reg, 31,  "axg_"#_name"_div",		\
+		     CLK_SET_RATE_PARENT)
+
+static AXG_MST_MCLK_GATE(mst_a_mclk,   AUDIO_MCLK_A_CTRL);
+static AXG_MST_MCLK_GATE(mst_b_mclk,   AUDIO_MCLK_B_CTRL);
+static AXG_MST_MCLK_GATE(mst_c_mclk,   AUDIO_MCLK_C_CTRL);
+static AXG_MST_MCLK_GATE(mst_d_mclk,   AUDIO_MCLK_D_CTRL);
+static AXG_MST_MCLK_GATE(mst_e_mclk,   AUDIO_MCLK_E_CTRL);
+static AXG_MST_MCLK_GATE(mst_f_mclk,   AUDIO_MCLK_F_CTRL);
+static AXG_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
+static AXG_MST_MCLK_GATE(spdifin_clk,  AUDIO_CLK_SPDIFIN_CTRL);
+static AXG_MST_MCLK_GATE(pdm_dclk,     AUDIO_CLK_PDMIN_CTRL0);
+static AXG_MST_MCLK_GATE(pdm_sysclk,   AUDIO_CLK_PDMIN_CTRL1);
+
+/* Sample Clocks */
+#define AXG_MST_SCLK_PRE_EN(_name, _reg)			\
+	AXG_AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31,	\
+		     "axg_mst_"#_name"_mclk", 0)
+
+static AXG_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width,		\
+			 _hi_shift, _hi_width, _pname, _iflags)		\
+struct clk_regmap axg_##_name = {					\
+	.data = &(struct meson_sclk_div_data) {				\
+		.div = {						\
+			.reg_off = (_reg),				\
+			.shift   = (_div_shift),			\
+			.width   = (_div_width),			\
+		},							\
+		.hi = {							\
+			.reg_off = (_reg),				\
+			.shift   = (_hi_shift),				\
+			.width   = (_hi_width),				\
+		},							\
+	},								\
+	.hw.init = &(struct clk_init_data) {				\
+		.name = "axg_"#_name,					\
+		.ops = &meson_sclk_div_ops,				\
+		.parent_names = (const char *[]) { _pname },		\
+		.num_parents = 1,					\
+		.flags = (_iflags),					\
+	},								\
+}
+
+#define AXG_MST_SCLK_DIV(_name, _reg)					\
+	AXG_AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0,	\
+			 "axg_mst_"#_name"_sclk_pre_en",		\
+			 CLK_SET_RATE_PARENT)
+
+static AXG_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_MST_SCLK_POST_EN(_name, _reg)				\
+	AXG_AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30,		\
+		     "axg_mst_"#_name"_sclk_div", CLK_SET_RATE_PARENT)
+
+static AXG_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
+			 _pname, _iflags)				\
+struct clk_regmap axg_##_name = {					\
+	.data = &(struct meson_clk_triphase_data) {			\
+		.ph0 = {						\
+			.reg_off = (_reg),				\
+			.shift   = (_shift0),				\
+			.width   = (_width),				\
+		},							\
+		.ph1 = {						\
+			.reg_off = (_reg),				\
+			.shift   = (_shift1),				\
+			.width   = (_width),				\
+		},							\
+		.ph2 = {						\
+			.reg_off = (_reg),				\
+			.shift   = (_shift2),				\
+			.width   = (_width),				\
+		},							\
+	},								\
+	.hw.init = &(struct clk_init_data) {				\
+		.name = "axg_"#_name,					\
+		.ops = &meson_clk_triphase_ops,				\
+		.parent_names = (const char *[]) { _pname },		\
+		.num_parents = 1,					\
+		.flags = CLK_DUTY_CYCLE_PARENT | (_iflags),		\
+	},								\
+}
+
+#define AXG_MST_SCLK(_name, _reg)					\
+	AXG_AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4,		\
+			 "axg_mst_"#_name"_sclk_post_en", CLK_SET_RATE_PARENT)
+
+static AXG_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1);
+static AXG_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1);
+static AXG_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1);
+static AXG_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1);
+static AXG_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1);
+static AXG_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1);
+
+#define AXG_MST_LRCLK_DIV(_name, _reg)					\
+	AXG_AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10,	\
+		    "axg_mst_"#_name"_sclk_post_en", 0)			\
+
+static AXG_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_MST_LRCLK(_name, _reg)					\
+	AXG_AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5,		\
+			 "axg_mst_"#_name"_lrclk_div", CLK_SET_RATE_PARENT)
+
+static AXG_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1);
+static AXG_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1);
+static AXG_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1);
+static AXG_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1);
+static AXG_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1);
+static AXG_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1);
+
+static const char * const tdm_sclk_parent_names[] = {
+	"axg_mst_a_sclk", "axg_mst_b_sclk", "axg_mst_c_sclk",
+	"axg_mst_d_sclk", "axg_mst_e_sclk", "axg_mst_f_sclk",
+	"axg_slv_sclk0", "axg_slv_sclk1", "axg_slv_sclk2",
+	"axg_slv_sclk3", "axg_slv_sclk4", "axg_slv_sclk5",
+	"axg_slv_sclk6", "axg_slv_sclk7", "axg_slv_sclk8",
+	"axg_slv_sclk9"
+};
+
+#define AXG_TDM_SCLK_MUX(_name, _reg)				\
+	AXG_AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24,	\
+		    CLK_MUX_ROUND_CLOSEST,			\
+		    tdm_sclk_parent_names, 0)
+
+static AXG_TDM_SCLK_MUX(in_a,  AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK_MUX(in_b,  AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK_MUX(in_c,  AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+#define AXG_TDM_SCLK_PRE_EN(_name, _reg)				\
+	AXG_AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31,		\
+		     "axg_tdm"#_name"_sclk_sel", CLK_SET_RATE_PARENT)
+
+static AXG_TDM_SCLK_PRE_EN(in_a,  AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK_PRE_EN(in_b,  AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK_PRE_EN(in_c,  AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+#define AXG_TDM_SCLK_POST_EN(_name, _reg)				\
+	AXG_AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30,		\
+		     "axg_tdm"#_name"_sclk_pre_en", CLK_SET_RATE_PARENT)
+
+static AXG_TDM_SCLK_POST_EN(in_a,  AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK_POST_EN(in_b,  AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK_POST_EN(in_c,  AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+#define AXG_TDM_SCLK(_name, _reg)					\
+	struct clk_regmap axg_tdm##_name##_sclk = {			\
+	.data = &(struct meson_clk_phase_data) {			\
+		.ph = {							\
+			.reg_off = (_reg),				\
+			.shift   = 29,					\
+			.width   = 1,					\
+		},							\
+	},								\
+	.hw.init = &(struct clk_init_data) {				\
+		.name = "axg_tdm"#_name"_sclk",				\
+		.ops = &meson_clk_phase_ops,				\
+		.parent_names = (const char *[])			\
+		{ "axg_tdm"#_name"_sclk_post_en" },			\
+		.num_parents = 1,					\
+		.flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT,	\
+	},								\
+}
+
+static AXG_TDM_SCLK(in_a,  AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK(in_b,  AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK(in_c,  AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+static const char * const tdm_lrclk_parent_names[] = {
+	"axg_mst_a_lrclk", "axg_mst_b_lrclk", "axg_mst_c_lrclk",
+	"axg_mst_d_lrclk", "axg_mst_e_lrclk", "axg_mst_f_lrclk",
+	"axg_slv_lrclk0", "axg_slv_lrclk1", "axg_slv_lrclk2",
+	"axg_slv_lrclk3", "axg_slv_lrclk4", "axg_slv_lrclk5",
+	"axg_slv_lrclk6", "axg_slv_lrclk7", "axg_slv_lrclk8",
+	"axg_slv_lrclk9"
+};
+
+#define AXG_TDM_LRLCK(_name, _reg)		       \
+	AXG_AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \
+		    CLK_MUX_ROUND_CLOSEST,	       \
+		    tdm_lrclk_parent_names, 0)
+
+static AXG_TDM_LRLCK(in_a,  AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_LRLCK(in_b,  AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_LRLCK(in_c,  AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+/*
+ * Array of all clocks provided by this provider
+ * The input clocks of the controller will be populated at runtime
+ */
+static struct clk_hw_onecell_data axg_audio_hw_onecell_data = {
+	.hws = {
+		[AUD_CLKID_DDR_ARB]		= &axg_ddr_arb.hw,
+		[AUD_CLKID_PDM]			= &axg_pdm.hw,
+		[AUD_CLKID_TDMIN_A]		= &axg_tdmin_a.hw,
+		[AUD_CLKID_TDMIN_B]		= &axg_tdmin_b.hw,
+		[AUD_CLKID_TDMIN_C]		= &axg_tdmin_c.hw,
+		[AUD_CLKID_TDMIN_LB]		= &axg_tdmin_lb.hw,
+		[AUD_CLKID_TDMOUT_A]		= &axg_tdmout_a.hw,
+		[AUD_CLKID_TDMOUT_B]		= &axg_tdmout_b.hw,
+		[AUD_CLKID_TDMOUT_C]		= &axg_tdmout_c.hw,
+		[AUD_CLKID_FRDDR_A]		= &axg_frddr_a.hw,
+		[AUD_CLKID_FRDDR_B]		= &axg_frddr_b.hw,
+		[AUD_CLKID_FRDDR_C]		= &axg_frddr_c.hw,
+		[AUD_CLKID_TODDR_A]		= &axg_toddr_a.hw,
+		[AUD_CLKID_TODDR_B]		= &axg_toddr_b.hw,
+		[AUD_CLKID_TODDR_C]		= &axg_toddr_c.hw,
+		[AUD_CLKID_LOOPBACK]		= &axg_loopback.hw,
+		[AUD_CLKID_SPDIFIN]		= &axg_spdifin.hw,
+		[AUD_CLKID_SPDIFOUT]		= &axg_spdifout.hw,
+		[AUD_CLKID_RESAMPLE]		= &axg_resample.hw,
+		[AUD_CLKID_POWER_DETECT]	= &axg_power_detect.hw,
+		[AUD_CLKID_MST_A_MCLK_SEL]	= &axg_mst_a_mclk_sel.hw,
+		[AUD_CLKID_MST_B_MCLK_SEL]	= &axg_mst_b_mclk_sel.hw,
+		[AUD_CLKID_MST_C_MCLK_SEL]	= &axg_mst_c_mclk_sel.hw,
+		[AUD_CLKID_MST_D_MCLK_SEL]	= &axg_mst_d_mclk_sel.hw,
+		[AUD_CLKID_MST_E_MCLK_SEL]	= &axg_mst_e_mclk_sel.hw,
+		[AUD_CLKID_MST_F_MCLK_SEL]	= &axg_mst_f_mclk_sel.hw,
+		[AUD_CLKID_MST_A_MCLK_DIV]	= &axg_mst_a_mclk_div.hw,
+		[AUD_CLKID_MST_B_MCLK_DIV]	= &axg_mst_b_mclk_div.hw,
+		[AUD_CLKID_MST_C_MCLK_DIV]	= &axg_mst_c_mclk_div.hw,
+		[AUD_CLKID_MST_D_MCLK_DIV]	= &axg_mst_d_mclk_div.hw,
+		[AUD_CLKID_MST_E_MCLK_DIV]	= &axg_mst_e_mclk_div.hw,
+		[AUD_CLKID_MST_F_MCLK_DIV]	= &axg_mst_f_mclk_div.hw,
+		[AUD_CLKID_MST_A_MCLK]		= &axg_mst_a_mclk.hw,
+		[AUD_CLKID_MST_B_MCLK]		= &axg_mst_b_mclk.hw,
+		[AUD_CLKID_MST_C_MCLK]		= &axg_mst_c_mclk.hw,
+		[AUD_CLKID_MST_D_MCLK]		= &axg_mst_d_mclk.hw,
+		[AUD_CLKID_MST_E_MCLK]		= &axg_mst_e_mclk.hw,
+		[AUD_CLKID_MST_F_MCLK]		= &axg_mst_f_mclk.hw,
+		[AUD_CLKID_SPDIFOUT_CLK_SEL]	= &axg_spdifout_clk_sel.hw,
+		[AUD_CLKID_SPDIFOUT_CLK_DIV]	= &axg_spdifout_clk_div.hw,
+		[AUD_CLKID_SPDIFOUT_CLK]	= &axg_spdifout_clk.hw,
+		[AUD_CLKID_SPDIFIN_CLK_SEL]	= &axg_spdifin_clk_sel.hw,
+		[AUD_CLKID_SPDIFIN_CLK_DIV]	= &axg_spdifin_clk_div.hw,
+		[AUD_CLKID_SPDIFIN_CLK]		= &axg_spdifin_clk.hw,
+		[AUD_CLKID_PDM_DCLK_SEL]	= &axg_pdm_dclk_sel.hw,
+		[AUD_CLKID_PDM_DCLK_DIV]	= &axg_pdm_dclk_div.hw,
+		[AUD_CLKID_PDM_DCLK]		= &axg_pdm_dclk.hw,
+		[AUD_CLKID_PDM_SYSCLK_SEL]	= &axg_pdm_sysclk_sel.hw,
+		[AUD_CLKID_PDM_SYSCLK_DIV]	= &axg_pdm_sysclk_div.hw,
+		[AUD_CLKID_PDM_SYSCLK]		= &axg_pdm_sysclk.hw,
+		[AUD_CLKID_MST_A_SCLK_PRE_EN]	= &axg_mst_a_sclk_pre_en.hw,
+		[AUD_CLKID_MST_B_SCLK_PRE_EN]	= &axg_mst_b_sclk_pre_en.hw,
+		[AUD_CLKID_MST_C_SCLK_PRE_EN]	= &axg_mst_c_sclk_pre_en.hw,
+		[AUD_CLKID_MST_D_SCLK_PRE_EN]	= &axg_mst_d_sclk_pre_en.hw,
+		[AUD_CLKID_MST_E_SCLK_PRE_EN]	= &axg_mst_e_sclk_pre_en.hw,
+		[AUD_CLKID_MST_F_SCLK_PRE_EN]	= &axg_mst_f_sclk_pre_en.hw,
+		[AUD_CLKID_MST_A_SCLK_DIV]	= &axg_mst_a_sclk_div.hw,
+		[AUD_CLKID_MST_B_SCLK_DIV]	= &axg_mst_b_sclk_div.hw,
+		[AUD_CLKID_MST_C_SCLK_DIV]	= &axg_mst_c_sclk_div.hw,
+		[AUD_CLKID_MST_D_SCLK_DIV]	= &axg_mst_d_sclk_div.hw,
+		[AUD_CLKID_MST_E_SCLK_DIV]	= &axg_mst_e_sclk_div.hw,
+		[AUD_CLKID_MST_F_SCLK_DIV]	= &axg_mst_f_sclk_div.hw,
+		[AUD_CLKID_MST_A_SCLK_POST_EN]	= &axg_mst_a_sclk_post_en.hw,
+		[AUD_CLKID_MST_B_SCLK_POST_EN]	= &axg_mst_b_sclk_post_en.hw,
+		[AUD_CLKID_MST_C_SCLK_POST_EN]	= &axg_mst_c_sclk_post_en.hw,
+		[AUD_CLKID_MST_D_SCLK_POST_EN]	= &axg_mst_d_sclk_post_en.hw,
+		[AUD_CLKID_MST_E_SCLK_POST_EN]	= &axg_mst_e_sclk_post_en.hw,
+		[AUD_CLKID_MST_F_SCLK_POST_EN]	= &axg_mst_f_sclk_post_en.hw,
+		[AUD_CLKID_MST_A_SCLK]		= &axg_mst_a_sclk.hw,
+		[AUD_CLKID_MST_B_SCLK]		= &axg_mst_b_sclk.hw,
+		[AUD_CLKID_MST_C_SCLK]		= &axg_mst_c_sclk.hw,
+		[AUD_CLKID_MST_D_SCLK]		= &axg_mst_d_sclk.hw,
+		[AUD_CLKID_MST_E_SCLK]		= &axg_mst_e_sclk.hw,
+		[AUD_CLKID_MST_F_SCLK]		= &axg_mst_f_sclk.hw,
+		[AUD_CLKID_MST_A_LRCLK_DIV]	= &axg_mst_a_lrclk_div.hw,
+		[AUD_CLKID_MST_B_LRCLK_DIV]	= &axg_mst_b_lrclk_div.hw,
+		[AUD_CLKID_MST_C_LRCLK_DIV]	= &axg_mst_c_lrclk_div.hw,
+		[AUD_CLKID_MST_D_LRCLK_DIV]	= &axg_mst_d_lrclk_div.hw,
+		[AUD_CLKID_MST_E_LRCLK_DIV]	= &axg_mst_e_lrclk_div.hw,
+		[AUD_CLKID_MST_F_LRCLK_DIV]	= &axg_mst_f_lrclk_div.hw,
+		[AUD_CLKID_MST_A_LRCLK]		= &axg_mst_a_lrclk.hw,
+		[AUD_CLKID_MST_B_LRCLK]		= &axg_mst_b_lrclk.hw,
+		[AUD_CLKID_MST_C_LRCLK]		= &axg_mst_c_lrclk.hw,
+		[AUD_CLKID_MST_D_LRCLK]		= &axg_mst_d_lrclk.hw,
+		[AUD_CLKID_MST_E_LRCLK]		= &axg_mst_e_lrclk.hw,
+		[AUD_CLKID_MST_F_LRCLK]		= &axg_mst_f_lrclk.hw,
+		[AUD_CLKID_TDMIN_A_SCLK_SEL]	= &axg_tdmin_a_sclk_sel.hw,
+		[AUD_CLKID_TDMIN_B_SCLK_SEL]	= &axg_tdmin_b_sclk_sel.hw,
+		[AUD_CLKID_TDMIN_C_SCLK_SEL]	= &axg_tdmin_c_sclk_sel.hw,
+		[AUD_CLKID_TDMIN_LB_SCLK_SEL]	= &axg_tdmin_lb_sclk_sel.hw,
+		[AUD_CLKID_TDMOUT_A_SCLK_SEL]	= &axg_tdmout_a_sclk_sel.hw,
+		[AUD_CLKID_TDMOUT_B_SCLK_SEL]	= &axg_tdmout_b_sclk_sel.hw,
+		[AUD_CLKID_TDMOUT_C_SCLK_SEL]	= &axg_tdmout_c_sclk_sel.hw,
+		[AUD_CLKID_TDMIN_A_SCLK_PRE_EN]	= &axg_tdmin_a_sclk_pre_en.hw,
+		[AUD_CLKID_TDMIN_B_SCLK_PRE_EN]	= &axg_tdmin_b_sclk_pre_en.hw,
+		[AUD_CLKID_TDMIN_C_SCLK_PRE_EN]	= &axg_tdmin_c_sclk_pre_en.hw,
+		[AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &axg_tdmin_lb_sclk_pre_en.hw,
+		[AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &axg_tdmout_a_sclk_pre_en.hw,
+		[AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &axg_tdmout_b_sclk_pre_en.hw,
+		[AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &axg_tdmout_c_sclk_pre_en.hw,
+		[AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &axg_tdmin_a_sclk_post_en.hw,
+		[AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &axg_tdmin_b_sclk_post_en.hw,
+		[AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &axg_tdmin_c_sclk_post_en.hw,
+		[AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &axg_tdmin_lb_sclk_post_en.hw,
+		[AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &axg_tdmout_a_sclk_post_en.hw,
+		[AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &axg_tdmout_b_sclk_post_en.hw,
+		[AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &axg_tdmout_c_sclk_post_en.hw,
+		[AUD_CLKID_TDMIN_A_SCLK]	= &axg_tdmin_a_sclk.hw,
+		[AUD_CLKID_TDMIN_B_SCLK]	= &axg_tdmin_b_sclk.hw,
+		[AUD_CLKID_TDMIN_C_SCLK]	= &axg_tdmin_c_sclk.hw,
+		[AUD_CLKID_TDMIN_LB_SCLK]	= &axg_tdmin_lb_sclk.hw,
+		[AUD_CLKID_TDMOUT_A_SCLK]	= &axg_tdmout_a_sclk.hw,
+		[AUD_CLKID_TDMOUT_B_SCLK]	= &axg_tdmout_b_sclk.hw,
+		[AUD_CLKID_TDMOUT_C_SCLK]	= &axg_tdmout_c_sclk.hw,
+		[AUD_CLKID_TDMIN_A_LRCLK]	= &axg_tdmin_a_lrclk.hw,
+		[AUD_CLKID_TDMIN_B_LRCLK]	= &axg_tdmin_b_lrclk.hw,
+		[AUD_CLKID_TDMIN_C_LRCLK]	= &axg_tdmin_c_lrclk.hw,
+		[AUD_CLKID_TDMIN_LB_LRCLK]	= &axg_tdmin_lb_lrclk.hw,
+		[AUD_CLKID_TDMOUT_A_LRCLK]	= &axg_tdmout_a_lrclk.hw,
+		[AUD_CLKID_TDMOUT_B_LRCLK]	= &axg_tdmout_b_lrclk.hw,
+		[AUD_CLKID_TDMOUT_C_LRCLK]	= &axg_tdmout_c_lrclk.hw,
+		[NR_CLKS] = NULL,
+	},
+	.num = NR_CLKS,
+};
+
+/* Convenience table to populate regmap in .probe() */
+static struct clk_regmap *const axg_audio_clk_regmaps[] = {
+	&axg_ddr_arb,
+	&axg_pdm,
+	&axg_tdmin_a,
+	&axg_tdmin_b,
+	&axg_tdmin_c,
+	&axg_tdmin_lb,
+	&axg_tdmout_a,
+	&axg_tdmout_b,
+	&axg_tdmout_c,
+	&axg_frddr_a,
+	&axg_frddr_b,
+	&axg_frddr_c,
+	&axg_toddr_a,
+	&axg_toddr_b,
+	&axg_toddr_c,
+	&axg_loopback,
+	&axg_spdifin,
+	&axg_spdifout,
+	&axg_resample,
+	&axg_power_detect,
+	&axg_mst_a_mclk_sel,
+	&axg_mst_b_mclk_sel,
+	&axg_mst_c_mclk_sel,
+	&axg_mst_d_mclk_sel,
+	&axg_mst_e_mclk_sel,
+	&axg_mst_f_mclk_sel,
+	&axg_mst_a_mclk_div,
+	&axg_mst_b_mclk_div,
+	&axg_mst_c_mclk_div,
+	&axg_mst_d_mclk_div,
+	&axg_mst_e_mclk_div,
+	&axg_mst_f_mclk_div,
+	&axg_mst_a_mclk,
+	&axg_mst_b_mclk,
+	&axg_mst_c_mclk,
+	&axg_mst_d_mclk,
+	&axg_mst_e_mclk,
+	&axg_mst_f_mclk,
+	&axg_spdifout_clk_sel,
+	&axg_spdifout_clk_div,
+	&axg_spdifout_clk,
+	&axg_spdifin_clk_sel,
+	&axg_spdifin_clk_div,
+	&axg_spdifin_clk,
+	&axg_pdm_dclk_sel,
+	&axg_pdm_dclk_div,
+	&axg_pdm_dclk,
+	&axg_pdm_sysclk_sel,
+	&axg_pdm_sysclk_div,
+	&axg_pdm_sysclk,
+	&axg_mst_a_sclk_pre_en,
+	&axg_mst_b_sclk_pre_en,
+	&axg_mst_c_sclk_pre_en,
+	&axg_mst_d_sclk_pre_en,
+	&axg_mst_e_sclk_pre_en,
+	&axg_mst_f_sclk_pre_en,
+	&axg_mst_a_sclk_div,
+	&axg_mst_b_sclk_div,
+	&axg_mst_c_sclk_div,
+	&axg_mst_d_sclk_div,
+	&axg_mst_e_sclk_div,
+	&axg_mst_f_sclk_div,
+	&axg_mst_a_sclk_post_en,
+	&axg_mst_b_sclk_post_en,
+	&axg_mst_c_sclk_post_en,
+	&axg_mst_d_sclk_post_en,
+	&axg_mst_e_sclk_post_en,
+	&axg_mst_f_sclk_post_en,
+	&axg_mst_a_sclk,
+	&axg_mst_b_sclk,
+	&axg_mst_c_sclk,
+	&axg_mst_d_sclk,
+	&axg_mst_e_sclk,
+	&axg_mst_f_sclk,
+	&axg_mst_a_lrclk_div,
+	&axg_mst_b_lrclk_div,
+	&axg_mst_c_lrclk_div,
+	&axg_mst_d_lrclk_div,
+	&axg_mst_e_lrclk_div,
+	&axg_mst_f_lrclk_div,
+	&axg_mst_a_lrclk,
+	&axg_mst_b_lrclk,
+	&axg_mst_c_lrclk,
+	&axg_mst_d_lrclk,
+	&axg_mst_e_lrclk,
+	&axg_mst_f_lrclk,
+	&axg_tdmin_a_sclk_sel,
+	&axg_tdmin_b_sclk_sel,
+	&axg_tdmin_c_sclk_sel,
+	&axg_tdmin_lb_sclk_sel,
+	&axg_tdmout_a_sclk_sel,
+	&axg_tdmout_b_sclk_sel,
+	&axg_tdmout_c_sclk_sel,
+	&axg_tdmin_a_sclk_pre_en,
+	&axg_tdmin_b_sclk_pre_en,
+	&axg_tdmin_c_sclk_pre_en,
+	&axg_tdmin_lb_sclk_pre_en,
+	&axg_tdmout_a_sclk_pre_en,
+	&axg_tdmout_b_sclk_pre_en,
+	&axg_tdmout_c_sclk_pre_en,
+	&axg_tdmin_a_sclk_post_en,
+	&axg_tdmin_b_sclk_post_en,
+	&axg_tdmin_c_sclk_post_en,
+	&axg_tdmin_lb_sclk_post_en,
+	&axg_tdmout_a_sclk_post_en,
+	&axg_tdmout_b_sclk_post_en,
+	&axg_tdmout_c_sclk_post_en,
+	&axg_tdmin_a_sclk,
+	&axg_tdmin_b_sclk,
+	&axg_tdmin_c_sclk,
+	&axg_tdmin_lb_sclk,
+	&axg_tdmout_a_sclk,
+	&axg_tdmout_b_sclk,
+	&axg_tdmout_c_sclk,
+	&axg_tdmin_a_lrclk,
+	&axg_tdmin_b_lrclk,
+	&axg_tdmin_c_lrclk,
+	&axg_tdmin_lb_lrclk,
+	&axg_tdmout_a_lrclk,
+	&axg_tdmout_b_lrclk,
+	&axg_tdmout_c_lrclk,
+};
+
+static struct clk *devm_clk_get_enable(struct device *dev, char *id)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get(dev, id);
+	if (IS_ERR(clk)) {
+		if (PTR_ERR(clk) != -EPROBE_DEFER)
+			dev_err(dev, "failed to get %s", id);
+		return clk;
+	}
+
+	ret = clk_prepare_enable(clk);
+	if (ret) {
+		dev_err(dev, "failed to enable %s", id);
+		return ERR_PTR(ret);
+	}
+
+	ret = devm_add_action_or_reset(dev,
+				       (void(*)(void *))clk_disable_unprepare,
+				       clk);
+	if (ret) {
+		dev_err(dev, "failed to add reset action on %s", id);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+static const struct clk_ops axg_clk_no_ops = {};
+
+static struct clk_hw *axg_clk_hw_register_bypass(struct device *dev,
+						 const char *name,
+						 const char *parent_name)
+{
+	struct clk_hw *hw;
+	struct clk_init_data init;
+	char *clk_name;
+	int ret;
+
+	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return ERR_PTR(-ENOMEM);
+
+	clk_name = kasprintf(GFP_KERNEL, "axg_%s", name);
+	if (!clk_name)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = clk_name;
+	init.ops = &axg_clk_no_ops;
+	init.flags = 0;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+	hw->init = &init;
+
+	ret = devm_clk_hw_register(dev, hw);
+	kfree(clk_name);
+
+	return ret ? ERR_PTR(ret) : hw;
+}
+
+static int axg_register_clk_hw_input(struct device *dev,
+				     const char *name,
+				     unsigned int clkid)
+{
+	struct clk *parent_clk = devm_clk_get(dev, name);
+	struct clk_hw *hw = NULL;
+
+	if (IS_ERR(parent_clk)) {
+		int err = PTR_ERR(parent_clk);
+
+		/* It is ok if an input clock is missing */
+		if (err == -ENOENT) {
+			dev_dbg(dev, "%s not provided", name);
+		} else {
+			if (err != -EPROBE_DEFER)
+				dev_err(dev, "failed to get %s clock", name);
+			return err;
+		}
+	} else {
+		hw = axg_clk_hw_register_bypass(dev, name,
+						__clk_get_name(parent_clk));
+	}
+
+	if (IS_ERR(hw)) {
+		dev_err(dev, "failed to register %s clock", name);
+		return PTR_ERR(hw);
+	}
+
+	axg_audio_hw_onecell_data.hws[clkid] = hw;
+	return 0;
+}
+
+static int axg_register_clk_hw_inputs(struct device *dev,
+				      const char *basename,
+				      unsigned int count,
+				      unsigned int clkid)
+{
+	char *name;
+	int i, ret;
+
+	for (i = 0; i < count; i++) {
+		name = kasprintf(GFP_KERNEL, "%s%d", basename, i);
+		if (!name)
+			return -ENOMEM;
+
+		ret = axg_register_clk_hw_input(dev, name, clkid + i);
+		kfree(name);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct regmap_config axg_audio_regmap_cfg = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= AUDIO_CLK_PDMIN_CTRL1,
+};
+
+static int axg_audio_clkc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct regmap *map;
+	struct resource *res;
+	void __iomem *regs;
+	struct clk *clk;
+	struct clk_hw *hw;
+	int ret, i;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg);
+	if (IS_ERR(map)) {
+		dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map));
+		return PTR_ERR(map);
+	}
+
+	/* Get the mandatory peripheral clock */
+	clk = devm_clk_get_enable(dev, "pclk");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ret = device_reset(dev);
+	if (ret) {
+		dev_err(dev, "failed to reset device\n");
+		return ret;
+	}
+
+	/* Register the peripheral input clock */
+	hw = axg_clk_hw_register_bypass(dev, "audio_pclk",
+					__clk_get_name(clk));
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+
+	axg_audio_hw_onecell_data.hws[AUD_CLKID_PCLK] = hw;
+
+	/* Register optional input master clocks */
+	ret = axg_register_clk_hw_inputs(dev, "mst_in",
+					 AXG_MST_IN_COUNT,
+					 AUD_CLKID_MST0);
+	if (ret)
+		return ret;
+
+	/* Register optional input slave sclks */
+	ret = axg_register_clk_hw_inputs(dev, "slv_sclk",
+					 AXG_SLV_SCLK_COUNT,
+					 AUD_CLKID_SLV_SCLK0);
+	if (ret)
+		return ret;
+
+	/* Register optional input slave lrclks */
+	ret = axg_register_clk_hw_inputs(dev, "slv_lrclk",
+					 AXG_SLV_LRCLK_COUNT,
+					 AUD_CLKID_SLV_LRCLK0);
+	if (ret)
+		return ret;
+
+	/* Populate regmap for the regmap backed clocks */
+	for (i = 0; i < ARRAY_SIZE(axg_audio_clk_regmaps); i++)
+		axg_audio_clk_regmaps[i]->map = map;
+
+	/* Take care to skip the registered input clocks */
+	for (i = AUD_CLKID_DDR_ARB; i < axg_audio_hw_onecell_data.num; i++) {
+		hw = axg_audio_hw_onecell_data.hws[i];
+		/* array might be sparse */
+		if (!hw)
+			continue;
+
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret) {
+			dev_err(dev, "failed to register clock %s\n",
+				hw->init->name);
+			return ret;
+		}
+	}
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+					   &axg_audio_hw_onecell_data);
+}
+
+static const struct of_device_id clkc_match_table[] = {
+	{ .compatible = "amlogic,axg-audio-clkc" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, clkc_match_table);
+
+static struct platform_driver axg_audio_driver = {
+	.probe		= axg_audio_clkc_probe,
+	.driver		= {
+		.name	= "axg-audio-clkc",
+		.of_match_table = clkc_match_table,
+	},
+};
+module_platform_driver(axg_audio_driver);
+
+MODULE_DESCRIPTION("Amlogic A113x Audio Clock driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h
new file mode 100644
index 000000000000..7191b39c9d65
--- /dev/null
+++ b/drivers/clk/meson/axg-audio.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __AXG_AUDIO_CLKC_H
+#define __AXG_AUDIO_CLKC_H
+
+/*
+ * Audio Clock  register offsets
+ *
+ * Register offsets from the datasheet must be multiplied by 4 before
+ * to get the right offset
+ */
+#define AUDIO_CLK_GATE_EN	0x000
+#define AUDIO_MCLK_A_CTRL	0x004
+#define AUDIO_MCLK_B_CTRL	0x008
+#define AUDIO_MCLK_C_CTRL	0x00C
+#define AUDIO_MCLK_D_CTRL	0x010
+#define AUDIO_MCLK_E_CTRL	0x014
+#define AUDIO_MCLK_F_CTRL	0x018
+#define AUDIO_MST_A_SCLK_CTRL0	0x040
+#define AUDIO_MST_A_SCLK_CTRL1	0x044
+#define AUDIO_MST_B_SCLK_CTRL0	0x048
+#define AUDIO_MST_B_SCLK_CTRL1	0x04C
+#define AUDIO_MST_C_SCLK_CTRL0	0x050
+#define AUDIO_MST_C_SCLK_CTRL1	0x054
+#define AUDIO_MST_D_SCLK_CTRL0	0x058
+#define AUDIO_MST_D_SCLK_CTRL1	0x05C
+#define AUDIO_MST_E_SCLK_CTRL0	0x060
+#define AUDIO_MST_E_SCLK_CTRL1	0x064
+#define AUDIO_MST_F_SCLK_CTRL0	0x068
+#define AUDIO_MST_F_SCLK_CTRL1	0x06C
+#define AUDIO_CLK_TDMIN_A_CTRL	0x080
+#define AUDIO_CLK_TDMIN_B_CTRL	0x084
+#define AUDIO_CLK_TDMIN_C_CTRL	0x088
+#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
+#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
+#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
+#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
+#define AUDIO_CLK_SPDIFIN_CTRL	0x09C
+#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
+#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
+#define AUDIO_CLK_LOCKER_CTRL	0x0A8
+#define AUDIO_CLK_PDMIN_CTRL0	0x0AC
+#define AUDIO_CLK_PDMIN_CTRL1	0x0B0
+
+/*
+ * CLKID index values
+ * These indices are entirely contrived and do not map onto the hardware.
+ */
+
+#define AUD_CLKID_PCLK			0
+#define AUD_CLKID_MST0			1
+#define AUD_CLKID_MST1			2
+#define AUD_CLKID_MST2			3
+#define AUD_CLKID_MST3			4
+#define AUD_CLKID_MST4			5
+#define AUD_CLKID_MST5			6
+#define AUD_CLKID_MST6			7
+#define AUD_CLKID_MST7			8
+#define AUD_CLKID_MST_A_MCLK_SEL	59
+#define AUD_CLKID_MST_B_MCLK_SEL	60
+#define AUD_CLKID_MST_C_MCLK_SEL	61
+#define AUD_CLKID_MST_D_MCLK_SEL	62
+#define AUD_CLKID_MST_E_MCLK_SEL	63
+#define AUD_CLKID_MST_F_MCLK_SEL	64
+#define AUD_CLKID_MST_A_MCLK_DIV	65
+#define AUD_CLKID_MST_B_MCLK_DIV	66
+#define AUD_CLKID_MST_C_MCLK_DIV	67
+#define AUD_CLKID_MST_D_MCLK_DIV	68
+#define AUD_CLKID_MST_E_MCLK_DIV	69
+#define AUD_CLKID_MST_F_MCLK_DIV	70
+#define AUD_CLKID_SPDIFOUT_CLK_SEL	71
+#define AUD_CLKID_SPDIFOUT_CLK_DIV	72
+#define AUD_CLKID_SPDIFIN_CLK_SEL	73
+#define AUD_CLKID_SPDIFIN_CLK_DIV	74
+#define AUD_CLKID_PDM_DCLK_SEL		75
+#define AUD_CLKID_PDM_DCLK_DIV		76
+#define AUD_CLKID_PDM_SYSCLK_SEL	77
+#define AUD_CLKID_PDM_SYSCLK_DIV	78
+#define AUD_CLKID_MST_A_SCLK_PRE_EN	92
+#define AUD_CLKID_MST_B_SCLK_PRE_EN	93
+#define AUD_CLKID_MST_C_SCLK_PRE_EN	94
+#define AUD_CLKID_MST_D_SCLK_PRE_EN	95
+#define AUD_CLKID_MST_E_SCLK_PRE_EN	96
+#define AUD_CLKID_MST_F_SCLK_PRE_EN	97
+#define AUD_CLKID_MST_A_SCLK_DIV	98
+#define AUD_CLKID_MST_B_SCLK_DIV	99
+#define AUD_CLKID_MST_C_SCLK_DIV	100
+#define AUD_CLKID_MST_D_SCLK_DIV	101
+#define AUD_CLKID_MST_E_SCLK_DIV	102
+#define AUD_CLKID_MST_F_SCLK_DIV	103
+#define AUD_CLKID_MST_A_SCLK_POST_EN	104
+#define AUD_CLKID_MST_B_SCLK_POST_EN	105
+#define AUD_CLKID_MST_C_SCLK_POST_EN	106
+#define AUD_CLKID_MST_D_SCLK_POST_EN	107
+#define AUD_CLKID_MST_E_SCLK_POST_EN	108
+#define AUD_CLKID_MST_F_SCLK_POST_EN	109
+#define AUD_CLKID_MST_A_LRCLK_DIV	110
+#define AUD_CLKID_MST_B_LRCLK_DIV	111
+#define AUD_CLKID_MST_C_LRCLK_DIV	112
+#define AUD_CLKID_MST_D_LRCLK_DIV	113
+#define AUD_CLKID_MST_E_LRCLK_DIV	114
+#define AUD_CLKID_MST_F_LRCLK_DIV	115
+#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN	137
+#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN	138
+#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN	139
+#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN	140
+#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN	141
+#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN	142
+#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN	143
+#define AUD_CLKID_TDMIN_A_SCLK_POST_EN	144
+#define AUD_CLKID_TDMIN_B_SCLK_POST_EN	145
+#define AUD_CLKID_TDMIN_C_SCLK_POST_EN	146
+#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN	147
+#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN	148
+#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN	149
+#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN	150
+
+/* include the CLKIDs which are part of the DT bindings */
+#include <dt-bindings/clock/axg-audio-clkc.h>
+
+#define NR_CLKS	151
+
+#endif /*__AXG_AUDIO_CLKC_H */
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 5/6] dt-bindings: clock: add meson axg audio clock controller bindings
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel
In-Reply-To: <20180522163457.13834-1-jbrunet@baylibre.com>

Export the clock ids dt-bindings usable by the consumers of the clock
controller and add the documentation for the device tree bindings of
the audio clock controller of the A113 based SoCs.

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 .../bindings/clock/amlogic,axg-audio-clkc.txt      | 56 +++++++++++++
 include/dt-bindings/clock/axg-audio-clkc.h         | 94 ++++++++++++++++++++++
 2 files changed, 150 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
 create mode 100644 include/dt-bindings/clock/axg-audio-clkc.h

diff --git a/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
new file mode 100644
index 000000000000..61777ad24f61
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
@@ -0,0 +1,56 @@
+* Amlogic AXG Audio Clock Controllers
+
+The Amlogic AXG audio clock controller generates and supplies clock to the
+other elements of the audio subsystem, such as fifos, i2s, spdif and pdm
+devices.
+
+Required Properties:
+
+- compatible	: should be "amlogic,axg-audio-clkc" for the A113X and A113D
+- reg		: physical base address of the clock controller and length of
+		  memory mapped region.
+- clocks	: a list of phandle + clock-specifier pairs for the clocks listed
+		  in clock-names.
+- clock-names	: must contain the following:
+		  * "pclk" - Main peripheral bus clock
+		  may contain the following:
+		  * "mst_in[0-7]" - 8 input plls to generate clock signals
+		  * "slv_sclk[0-9]" - 10 slave bit clocks provided by external
+				      components.
+		  * "slv_lrclk[0-9]" - 10 slave sample clocks provided by external
+				       components.
+- resets	: phandle of the internal reset line
+- #clock-cells	: should be 1.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. All available clocks are defined as
+preprocessor macros in the dt-bindings/clock/axg-audio-clkc.h header and can be
+used in device tree sources.
+
+Example:
+
+clkc_audio: clock-controller@0 {
+	compatible = "amlogic,axg-audio-clkc";
+	reg = <0x0 0x0 0x0 0xb4>;
+	#clock-cells = <1>;
+
+	clocks = <&clkc CLKID_AUDIO>,
+		 <&clkc CLKID_MPLL0>,
+		 <&clkc CLKID_MPLL1>,
+		 <&clkc CLKID_MPLL2>,
+		 <&clkc CLKID_MPLL3>,
+		 <&clkc CLKID_HIFI_PLL>,
+		 <&clkc CLKID_FCLK_DIV3>,
+		 <&clkc CLKID_FCLK_DIV4>,
+		 <&clkc CLKID_GP0_PLL>;
+	clock-names = "pclk",
+		      "mst_in0",
+		      "mst_in1",
+		      "mst_in2",
+		      "mst_in3",
+		      "mst_in4",
+		      "mst_in5",
+		      "mst_in6",
+		      "mst_in7";
+	resets = <&reset RESET_AUDIO>;
+};
diff --git a/include/dt-bindings/clock/axg-audio-clkc.h b/include/dt-bindings/clock/axg-audio-clkc.h
new file mode 100644
index 000000000000..fd9c362099d9
--- /dev/null
+++ b/include/dt-bindings/clock/axg-audio-clkc.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2018 Baylibre SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __AXG_AUDIO_CLKC_BINDINGS_H
+#define __AXG_AUDIO_CLKC_BINDINGS_H
+
+#define AUD_CLKID_SLV_SCLK0		9
+#define AUD_CLKID_SLV_SCLK1		10
+#define AUD_CLKID_SLV_SCLK2		11
+#define AUD_CLKID_SLV_SCLK3		12
+#define AUD_CLKID_SLV_SCLK4		13
+#define AUD_CLKID_SLV_SCLK5		14
+#define AUD_CLKID_SLV_SCLK6		15
+#define AUD_CLKID_SLV_SCLK7		16
+#define AUD_CLKID_SLV_SCLK8		17
+#define AUD_CLKID_SLV_SCLK9		18
+#define AUD_CLKID_SLV_LRCLK0		19
+#define AUD_CLKID_SLV_LRCLK1		20
+#define AUD_CLKID_SLV_LRCLK2		21
+#define AUD_CLKID_SLV_LRCLK3		22
+#define AUD_CLKID_SLV_LRCLK4		23
+#define AUD_CLKID_SLV_LRCLK5		24
+#define AUD_CLKID_SLV_LRCLK6		25
+#define AUD_CLKID_SLV_LRCLK7		26
+#define AUD_CLKID_SLV_LRCLK8		27
+#define AUD_CLKID_SLV_LRCLK9		28
+#define AUD_CLKID_DDR_ARB		29
+#define AUD_CLKID_PDM			30
+#define AUD_CLKID_TDMIN_A		31
+#define AUD_CLKID_TDMIN_B		32
+#define AUD_CLKID_TDMIN_C		33
+#define AUD_CLKID_TDMIN_LB		34
+#define AUD_CLKID_TDMOUT_A		35
+#define AUD_CLKID_TDMOUT_B		36
+#define AUD_CLKID_TDMOUT_C		37
+#define AUD_CLKID_FRDDR_A		38
+#define AUD_CLKID_FRDDR_B		39
+#define AUD_CLKID_FRDDR_C		40
+#define AUD_CLKID_TODDR_A		41
+#define AUD_CLKID_TODDR_B		42
+#define AUD_CLKID_TODDR_C		43
+#define AUD_CLKID_LOOPBACK		44
+#define AUD_CLKID_SPDIFIN		45
+#define AUD_CLKID_SPDIFOUT		46
+#define AUD_CLKID_RESAMPLE		47
+#define AUD_CLKID_POWER_DETECT		48
+#define AUD_CLKID_MST_A_MCLK		49
+#define AUD_CLKID_MST_B_MCLK		50
+#define AUD_CLKID_MST_C_MCLK		51
+#define AUD_CLKID_MST_D_MCLK		52
+#define AUD_CLKID_MST_E_MCLK		53
+#define AUD_CLKID_MST_F_MCLK		54
+#define AUD_CLKID_SPDIFOUT_CLK		55
+#define AUD_CLKID_SPDIFIN_CLK		56
+#define AUD_CLKID_PDM_DCLK		57
+#define AUD_CLKID_PDM_SYSCLK		58
+#define AUD_CLKID_MST_A_SCLK		79
+#define AUD_CLKID_MST_B_SCLK		80
+#define AUD_CLKID_MST_C_SCLK		81
+#define AUD_CLKID_MST_D_SCLK		82
+#define AUD_CLKID_MST_E_SCLK		83
+#define AUD_CLKID_MST_F_SCLK		84
+#define AUD_CLKID_MST_A_LRCLK		86
+#define AUD_CLKID_MST_B_LRCLK		87
+#define AUD_CLKID_MST_C_LRCLK		88
+#define AUD_CLKID_MST_D_LRCLK		89
+#define AUD_CLKID_MST_E_LRCLK		90
+#define AUD_CLKID_MST_F_LRCLK		91
+#define AUD_CLKID_TDMIN_A_SCLK_SEL	116
+#define AUD_CLKID_TDMIN_B_SCLK_SEL	117
+#define AUD_CLKID_TDMIN_C_SCLK_SEL	118
+#define AUD_CLKID_TDMIN_LB_SCLK_SEL	119
+#define AUD_CLKID_TDMOUT_A_SCLK_SEL	120
+#define AUD_CLKID_TDMOUT_B_SCLK_SEL	121
+#define AUD_CLKID_TDMOUT_C_SCLK_SEL	122
+#define AUD_CLKID_TDMIN_A_SCLK		123
+#define AUD_CLKID_TDMIN_B_SCLK		124
+#define AUD_CLKID_TDMIN_C_SCLK		125
+#define AUD_CLKID_TDMIN_LB_SCLK		126
+#define AUD_CLKID_TDMOUT_A_SCLK		127
+#define AUD_CLKID_TDMOUT_B_SCLK		128
+#define AUD_CLKID_TDMOUT_C_SCLK		129
+#define AUD_CLKID_TDMIN_A_LRCLK		130
+#define AUD_CLKID_TDMIN_B_LRCLK		131
+#define AUD_CLKID_TDMIN_C_LRCLK		132
+#define AUD_CLKID_TDMIN_LB_LRCLK	133
+#define AUD_CLKID_TDMOUT_A_LRCLK	134
+#define AUD_CLKID_TDMOUT_B_LRCLK	135
+#define AUD_CLKID_TDMOUT_C_LRCLK	136
+
+#endif /* __AXG_AUDIO_CLKC_BINDINGS_H */
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 4/6] clk: meson: add axg audio sclk divider driver
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel
In-Reply-To: <20180522163457.13834-1-jbrunet@baylibre.com>

Add a driver to control the clock divider found in the sample clock
generator of the axg audio clock controller.

The sclk divider accumulates specific features which make the generic
divider unsuitable to control it:
- zero based divider (div = val + 1), but zero value gates the clock,
  so minimum divider value is 2.
- lrclk variant may adjust the duty cycle depending the divider value
  and the 'hi' value.

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/meson/Makefile     |   2 +-
 drivers/clk/meson/clkc-audio.h |   8 ++
 drivers/clk/meson/sclk-div.c   | 243 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 252 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/meson/sclk-div.c

diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 74ec070b0974..1574e3f0de5c 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
 obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
-obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO)	+= clk-triphase.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO)	+= clk-triphase.o sclk-div.o
 obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
 obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
 obj-$(CONFIG_COMMON_CLK_GXBB)	 += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
index 286ff1201258..0a7c157ebf81 100644
--- a/drivers/clk/meson/clkc-audio.h
+++ b/drivers/clk/meson/clkc-audio.h
@@ -15,6 +15,14 @@ struct meson_clk_triphase_data {
 	struct parm ph2;
 };
 
+struct meson_sclk_div_data {
+	struct parm div;
+	struct parm hi;
+	unsigned int cached_div;
+	struct clk_duty cached_duty;
+};
+
 extern const struct clk_ops meson_clk_triphase_ops;
+extern const struct clk_ops meson_sclk_div_ops;
 
 #endif /* __MESON_CLKC_AUDIO_H */
diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c
new file mode 100644
index 000000000000..bc64019b8eeb
--- /dev/null
+++ b/drivers/clk/meson/sclk-div.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
+ * Sample clock generator divider:
+ * This HW divider gates with value 0 but is otherwise a zero based divider:
+ *
+ * val >= 1
+ * divider = val + 1
+ *
+ * The duty cycle may also be set for the LR clock variant. The duty cycle
+ * ratio is:
+ *
+ * hi = [0 - val]
+ * duty_cycle = (1 + hi) / (1 + val)
+ */
+
+#include "clkc-audio.h"
+
+static inline struct meson_sclk_div_data *
+meson_sclk_div_data(struct clk_regmap *clk)
+{
+	return (struct meson_sclk_div_data *)clk->data;
+}
+
+static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
+{
+	return (1 << sclk->div.width) - 1;
+}
+
+static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
+{
+	return sclk_div_maxval(sclk) + 1;
+}
+
+static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
+			   unsigned long prate, int maxdiv)
+{
+	int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
+
+	return clamp(div, 2, maxdiv);
+}
+
+static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
+			    unsigned long *prate,
+			    struct meson_sclk_div_data *sclk)
+{
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+	int bestdiv = 0, i;
+	unsigned long maxdiv, now, parent_now;
+	unsigned long best = 0, best_parent = 0;
+
+	if (!rate)
+		rate = 1;
+
+	maxdiv = sclk_div_maxdiv(sclk);
+
+	if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
+		return sclk_div_getdiv(hw, rate, *prate, maxdiv);
+
+	/*
+	 * The maximum divider we can use without overflowing
+	 * unsigned long in rate * i below
+	 */
+	maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+	for (i = 2; i <= maxdiv; i++) {
+		/*
+		 * It's the most ideal case if the requested rate can be
+		 * divided from parent clock without needing to change
+		 * parent rate, so return the divider immediately.
+		 */
+		if (rate * i == *prate)
+			return i;
+
+		parent_now = clk_hw_round_rate(parent, rate * i);
+		now = DIV_ROUND_UP_ULL((u64)parent_now, i);
+
+		if (abs(rate - now) < abs(rate - best)) {
+			bestdiv = i;
+			best = now;
+			best_parent = parent_now;
+		}
+	}
+
+	if (!bestdiv)
+		bestdiv = sclk_div_maxdiv(sclk);
+	else
+		*prate = best_parent;
+
+	return bestdiv;
+}
+
+static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+	int div;
+
+	div = sclk_div_bestdiv(hw, rate, prate, sclk);
+
+	return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+
+static void sclk_apply_ratio(struct clk_regmap *clk,
+			     struct meson_sclk_div_data *sclk)
+{
+	unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
+					    sclk->cached_duty.num,
+					    sclk->cached_duty.den);
+
+	if (hi)
+		hi -= 1;
+
+	meson_parm_write(clk->map, &sclk->hi, hi);
+}
+
+static int sclk_div_set_duty_cycle(struct clk_hw *hw,
+				   struct clk_duty *duty)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+	if (MESON_PARM_APPLICABLE(&sclk->hi)) {
+		memcpy(&sclk->cached_duty, duty, sizeof(*duty));
+		sclk_apply_ratio(clk, sclk);
+	}
+
+	return 0;
+}
+
+static int sclk_div_get_duty_cycle(struct clk_hw *hw,
+				   struct clk_duty *duty)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+	int hi;
+
+	if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
+		duty->num = 1;
+		duty->den = 2;
+		return 0;
+	}
+
+	hi = meson_parm_read(clk->map, &sclk->hi);
+	duty->num = hi + 1;
+	duty->den = sclk->cached_div;
+	return 0;
+}
+
+static void sclk_apply_divider(struct clk_regmap *clk,
+			       struct meson_sclk_div_data *sclk)
+{
+	if (MESON_PARM_APPLICABLE(&sclk->hi))
+		sclk_apply_ratio(clk, sclk);
+
+	meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
+}
+
+static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long prate)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+	unsigned long maxdiv = sclk_div_maxdiv(sclk);
+
+	sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
+
+	if (clk_hw_is_enabled(hw))
+		sclk_apply_divider(clk, sclk);
+
+	return 0;
+}
+
+static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
+					  unsigned long prate)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+	return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
+}
+
+static int sclk_div_enable(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+	sclk_apply_divider(clk, sclk);
+
+	return 0;
+}
+
+static void sclk_div_disable(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+	meson_parm_write(clk->map, &sclk->div, 0);
+}
+
+static int sclk_div_is_enabled(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+	if (meson_parm_read(clk->map, &sclk->div))
+		return 1;
+
+	return 0;
+}
+
+static void sclk_div_init(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+	unsigned int val;
+
+	val = meson_parm_read(clk->map, &sclk->div);
+
+	/* if the divider is initially disabled, assume max */
+	if (!val)
+		sclk->cached_div = sclk_div_maxdiv(sclk);
+	else
+		sclk->cached_div = val + 1;
+
+	sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
+}
+
+const struct clk_ops meson_sclk_div_ops = {
+	.recalc_rate	= sclk_div_recalc_rate,
+	.round_rate	= sclk_div_round_rate,
+	.set_rate	= sclk_div_set_rate,
+	.enable		= sclk_div_enable,
+	.disable	= sclk_div_disable,
+	.is_enabled	= sclk_div_is_enabled,
+	.get_duty_cycle	= sclk_div_get_duty_cycle,
+	.set_duty_cycle = sclk_div_set_duty_cycle,
+	.init		= sclk_div_init,
+};
+EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 3/6] clk: meson: add triple phase clock driver
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel
In-Reply-To: <20180522163457.13834-1-jbrunet@baylibre.com>

Add a driver to control the output of the sample clock generator found
in the axg audio clock controller.

The goal of this driver is to coherently control the phase provided to
the different element using the sample clock generator. This simplify
the usage of the sample clock generator a lot, without comprising the
ability of the SoC.

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/meson/Kconfig        |  5 +++
 drivers/clk/meson/Makefile       |  1 +
 drivers/clk/meson/clk-triphase.c | 68 ++++++++++++++++++++++++++++++++++++++++
 drivers/clk/meson/clkc-audio.h   | 20 ++++++++++++
 4 files changed, 94 insertions(+)
 create mode 100644 drivers/clk/meson/clk-triphase.c
 create mode 100644 drivers/clk/meson/clkc-audio.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 9d00809f07fc..9ad95966aa3d 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -3,6 +3,11 @@ config COMMON_CLK_AMLOGIC
 	depends on ARCH_MESON || COMPILE_TEST
 	select COMMON_CLK_REGMAP_MESON
 
+config COMMON_CLK_AMLOGIC_AUDIO
+	bool
+	depends on ARCH_MESON || COMPILE_TEST
+	select COMMON_CLK_AMLOGIC
+
 config COMMON_CLK_MESON_AO
 	bool
 	depends on OF
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 162e4a6f5c55..74ec070b0974 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
 obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO)	+= clk-triphase.o
 obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
 obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
 obj-$(CONFIG_COMMON_CLK_GXBB)	 += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c
new file mode 100644
index 000000000000..4a59936251e5
--- /dev/null
+++ b/drivers/clk/meson/clk-triphase.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/clk-provider.h>
+#include "clkc-audio.h"
+
+/*
+ * This is a special clock for the audio controller.
+ * The phase of mst_sclk clock output can be controlled independently
+ * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
+ * Controlling these 3 phases as just one makes things simpler and
+ * give the same clock view to all the element on the i2s bus.
+ * If necessary, we can still control the phase in the tdm block
+ * which makes these independent control redundant.
+ */
+static inline struct meson_clk_triphase_data *
+meson_clk_triphase_data(struct clk_regmap *clk)
+{
+	return (struct meson_clk_triphase_data *)clk->data;
+}
+
+static void meson_clk_triphase_sync(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+	unsigned int val;
+
+	/* Get phase 0 and sync it to phase 1 and 2 */
+	val = meson_parm_read(clk->map, &tph->ph0);
+	meson_parm_write(clk->map, &tph->ph1, val);
+	meson_parm_write(clk->map, &tph->ph2, val);
+}
+
+static int meson_clk_triphase_get_phase(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+	unsigned int val;
+
+	/* Phase are in sync, reading phase 0 is enough */
+	val = meson_parm_read(clk->map, &tph->ph0);
+
+	return meson_clk_degrees_from_val(val, tph->ph0.width);
+}
+
+static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+	unsigned int val;
+
+	val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
+	meson_parm_write(clk->map, &tph->ph0, val);
+	meson_parm_write(clk->map, &tph->ph1, val);
+	meson_parm_write(clk->map, &tph->ph2, val);
+
+	return 0;
+}
+
+const struct clk_ops meson_clk_triphase_ops = {
+	.init		= meson_clk_triphase_sync,
+	.get_phase	= meson_clk_triphase_get_phase,
+	.set_phase	= meson_clk_triphase_set_phase,
+};
+EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
new file mode 100644
index 000000000000..286ff1201258
--- /dev/null
+++ b/drivers/clk/meson/clkc-audio.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_CLKC_AUDIO_H
+#define __MESON_CLKC_AUDIO_H
+
+#include "clkc.h"
+
+struct meson_clk_triphase_data {
+	struct parm ph0;
+	struct parm ph1;
+	struct parm ph2;
+};
+
+extern const struct clk_ops meson_clk_triphase_ops;
+
+#endif /* __MESON_CLKC_AUDIO_H */
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 2/6] clk: meson: add clk-phase clock driver
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel
In-Reply-To: <20180522163457.13834-1-jbrunet@baylibre.com>

Add a driver based meson clk-regmap to control clock phase on
amlogic SoCs

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/meson/Makefile    |  1 +
 drivers/clk/meson/clk-phase.c | 63 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/meson/clkc.h      |  8 ++++++
 3 files changed, 72 insertions(+)
 create mode 100644 drivers/clk/meson/clk-phase.c

diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index d0d13aeb369a..162e4a6f5c55 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
 obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
 obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
 obj-$(CONFIG_COMMON_CLK_GXBB)	 += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c
new file mode 100644
index 000000000000..cba43748ce3d
--- /dev/null
+++ b/drivers/clk/meson/clk-phase.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/clk-provider.h>
+#include "clkc.h"
+
+#define phase_step(_width) (360 / (1 << (_width)))
+
+static inline struct meson_clk_phase_data *
+meson_clk_phase_data(struct clk_regmap *clk)
+{
+	return (struct meson_clk_phase_data *)clk->data;
+}
+
+int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
+{
+	return phase_step(width) * val;
+}
+EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
+
+unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
+{
+	unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
+
+	/*
+	 * This last calculation is here for cases when degrees is rounded
+	 * to 360, in which case val == (1 << width).
+	 */
+	return val % (1 << width);
+}
+EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
+
+static int meson_clk_phase_get_phase(struct clk_hw *hw)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
+	unsigned int val;
+
+	val = meson_parm_read(clk->map, &phase->ph);
+
+	return meson_clk_degrees_from_val(val, phase->ph.width);
+}
+
+static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
+{
+	struct clk_regmap *clk = to_clk_regmap(hw);
+	struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
+	unsigned int val;
+
+	val = meson_clk_degrees_to_val(degrees, phase->ph.width);
+	meson_parm_write(clk->map, &phase->ph, val);
+
+	return 0;
+}
+
+const struct clk_ops meson_clk_phase_ops = {
+	.get_phase	= meson_clk_phase_get_phase,
+	.set_phase	= meson_clk_phase_set_phase,
+};
+EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 2fb084330ee9..fd520ccdd1be 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -96,6 +96,13 @@ struct meson_clk_audio_div_data {
 	u8 flags;
 };
 
+struct meson_clk_phase_data {
+	struct parm ph;
+};
+
+int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
+unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
+
 #define MESON_GATE(_name, _reg, _bit)					\
 struct clk_regmap _name = {						\
 	.data = &(struct clk_regmap_gate_data){				\
@@ -119,5 +126,6 @@ extern const struct clk_ops meson_clk_mpll_ro_ops;
 extern const struct clk_ops meson_clk_mpll_ops;
 extern const struct clk_ops meson_clk_audio_divider_ro_ops;
 extern const struct clk_ops meson_clk_audio_divider_ops;
+extern const struct clk_ops meson_clk_phase_ops;
 
 #endif /* __CLKC_H */
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 1/6] clk: meson: clean-up meson clock configuration
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel
In-Reply-To: <20180522163457.13834-1-jbrunet@baylibre.com>

Clean the dependencies in meson clock Kconfig.
CLK_AMLOGIC should actually select CLK_REGMAP_MESON which it uses. Also,
each platform should select CLK_AMLOGIC, so everything is properly turned
on when the platform Kconfig enable each configuration flag

Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/meson/Kconfig | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 815659eebea3..9d00809f07fc 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -1,13 +1,14 @@
 config COMMON_CLK_AMLOGIC
 	bool
-	depends on OF
 	depends on ARCH_MESON || COMPILE_TEST
+	select COMMON_CLK_REGMAP_MESON
 
 config COMMON_CLK_MESON_AO
 	bool
 	depends on OF
 	depends on ARCH_MESON || COMPILE_TEST
 	select COMMON_CLK_REGMAP_MESON
+	select RESET_CONTROLLER
 
 config COMMON_CLK_REGMAP_MESON
 	bool
@@ -15,9 +16,8 @@ config COMMON_CLK_REGMAP_MESON
 
 config COMMON_CLK_MESON8B
 	bool
-	depends on COMMON_CLK_AMLOGIC
+	select COMMON_CLK_AMLOGIC
 	select RESET_CONTROLLER
-	select COMMON_CLK_REGMAP_MESON
 	help
 	  Support for the clock controller on AmLogic S802 (Meson8),
 	  S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
@@ -25,10 +25,8 @@ config COMMON_CLK_MESON8B
 
 config COMMON_CLK_GXBB
 	bool
-	depends on COMMON_CLK_AMLOGIC
-	select RESET_CONTROLLER
+	select COMMON_CLK_AMLOGIC
 	select COMMON_CLK_MESON_AO
-	select COMMON_CLK_REGMAP_MESON
 	select MFD_SYSCON
 	help
 	  Support for the clock controller on AmLogic S905 devices, aka gxbb.
@@ -36,10 +34,8 @@ config COMMON_CLK_GXBB
 
 config COMMON_CLK_AXG
 	bool
-	depends on COMMON_CLK_AMLOGIC
-	select RESET_CONTROLLER
+	select COMMON_CLK_AMLOGIC
 	select COMMON_CLK_MESON_AO
-	select COMMON_CLK_REGMAP_MESON
 	select MFD_SYSCON
 	help
 	  Support for the clock controller on AmLogic A113D devices, aka axg.
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 0/6] clk: meson: axg: add audio clock controller support
From: Jerome Brunet @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Neil Armstrong, Carlo Caione, Kevin Hilman
  Cc: Jerome Brunet, Michael Turquette, Stephen Boyd, linux-amlogic,
	linux-clk, devicetree, linux-kernel

The purpose of this patchset is to add support for the clock controller
dedicated to the audio subsystem found on A113 based SoCs

The series depends on the CLK_MUX_ROUND_CLOSEST fixes [merged in clk-next]
and the duty cycle support [1] making their way into CCF.

First patch is a clean-up of the meson clk Kconfig.
Then, there is 3 clock provider drivers for clocks found in the audio
The last 3 are for the clock controller itself.

Changes since v1: [2]
 * Rebase clk-meson's next/drivers
 * Correct typo in documentation and squash DT patches

[1]: https://lkml.kernel.org/r/20180420211141.28929-1-jbrunet@baylibre.com
[2]: https://lkml.kernel.org/r/20180425163304.10852-1-jbrunet@baylibre.com

Jerome Brunet (6):
  clk: meson: clean-up meson clock configuration
  clk: meson: add clk-phase clock driver
  clk: meson: add triple phase clock driver
  clk: meson: add axg audio sclk divider driver
  dt-bindings: clock: add meson axg audio clock controller bindings
  clk: meson: axg: add the audio clock controller driver

 .../bindings/clock/amlogic,axg-audio-clkc.txt      |  56 ++
 drivers/clk/meson/Kconfig                          |  28 +-
 drivers/clk/meson/Makefile                         |   3 +
 drivers/clk/meson/axg-audio.c                      | 845 +++++++++++++++++++++
 drivers/clk/meson/axg-audio.h                      | 127 ++++
 drivers/clk/meson/clk-phase.c                      |  63 ++
 drivers/clk/meson/clk-triphase.c                   |  68 ++
 drivers/clk/meson/clkc-audio.h                     |  28 +
 drivers/clk/meson/clkc.h                           |   8 +
 drivers/clk/meson/sclk-div.c                       | 243 ++++++
 include/dt-bindings/clock/axg-audio-clkc.h         |  94 +++
 11 files changed, 1554 insertions(+), 9 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
 create mode 100644 drivers/clk/meson/axg-audio.c
 create mode 100644 drivers/clk/meson/axg-audio.h
 create mode 100644 drivers/clk/meson/clk-phase.c
 create mode 100644 drivers/clk/meson/clk-triphase.c
 create mode 100644 drivers/clk/meson/clkc-audio.h
 create mode 100644 drivers/clk/meson/sclk-div.c
 create mode 100644 include/dt-bindings/clock/axg-audio-clkc.h

-- 
2.14.3

^ permalink raw reply

* Re: [PATCH v4 1/3] dt-bindings: media: rcar-vin: Add R8A77995 support
From: Rob Herring @ 2018-05-22 16:34 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: niklas.soderlund, laurent.pinchart, horms, geert, magnus.damm,
	linux-renesas-soc, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <1526913942-15426-2-git-send-email-jacopo+renesas@jmondi.org>

On Mon, May 21, 2018 at 04:45:40PM +0200, Jacopo Mondi wrote:
> Add compatible string for R-Car D3 R8A7795 to list of SoCs supported by
> rcar-vin driver.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Acked-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
> ---
>  Documentation/devicetree/bindings/media/rcar_vin.txt | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply

* Re: [PATCH v4 2/3] dt-bindings: thermal: rcar-thermal: add R8A77995 support
From: Rob Herring @ 2018-05-22 16:30 UTC (permalink / raw)
  To: Yoshihiro Kaneko
  Cc: linux-renesas-soc, Zhang Rui, Eduardo Valentin, Simon Horman,
	Magnus Damm, linux-pm, devicetree
In-Reply-To: <1526808379-3850-3-git-send-email-ykaneko0929@gmail.com>

On Sun, May 20, 2018 at 06:26:18PM +0900, Yoshihiro Kaneko wrote:
> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> ---
>  Documentation/devicetree/bindings/thermal/rcar-thermal.txt | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply

* Re: [PATCH v5 1/4] dt-bindings: pinctrl: qcom: add gpio-ranges, gpio-reserved-ranges
From: Rob Herring @ 2018-05-22 16:29 UTC (permalink / raw)
  To: Christian Lamparter
  Cc: Mark Rutland, devicetree, David Brown, Stephen Boyd,
	linux-arm-msm, Linus Walleij, Bjorn Andersson, linux-gpio,
	Sven Eckelmann, Andy Gross, linux-arm-kernel
In-Reply-To: <910e5a85a6a8020069996e2ff397c93e9c5fe18c.1526935804.git.chunkeey@gmail.com>

On Mon, May 21, 2018 at 10:57:36PM +0200, Christian Lamparter wrote:
> This patch adds the gpio-ranges and gpio-reserved-ranges property
> definitions to the binding text files supported by the pinctrl-msm
> driver framework.
> 
> gpio-ranges:
> For DT-based platforms the pinctrl-msm framework currently relies
> on the deprecated-for-DT gpiochip_add_pin_range() function to add
> the range of GPIOs to be handled by the pin controller. Due to
> interactions within gpiolib code, this causes the pinctrl-msm
> driver to bail out (-517) during boot when a gpio-hog is declared.
> This can be fatal and cause the system to not boot or reset
> (for a detailed explanation and call-trace, refer to patch:
> "pinctrl: msm: fix gpio-hog related boot issues" in this series).
> 
> gpio-reserved-ranges:
> The binding has been added as a precaution since the TrustZone
> firmware (aka QSEE), which is running as the hypervisor, might
> have reserved certain, but undisclosed pins. Hence reading or
> writing to the registers for those pins will cause an
> XPU violation and this subsequently crashes the kernel.
> 
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> ---
>  .../bindings/pinctrl/qcom,apq8064-pinctrl.txt         |  6 ++++++
>  .../bindings/pinctrl/qcom,apq8084-pinctrl.txt         | 11 +++++++++++
>  .../bindings/pinctrl/qcom,ipq4019-pinctrl.txt         |  6 ++++++
>  .../bindings/pinctrl/qcom,ipq8064-pinctrl.txt         |  6 ++++++
>  .../bindings/pinctrl/qcom,ipq8074-pinctrl.txt         | 10 ++++++++++
>  .../bindings/pinctrl/qcom,mdm9615-pinctrl.txt         | 11 +++++++++++
>  .../bindings/pinctrl/qcom,msm8660-pinctrl.txt         |  6 ++++++
>  .../bindings/pinctrl/qcom,msm8916-pinctrl.txt         | 11 +++++++++++
>  .../bindings/pinctrl/qcom,msm8960-pinctrl.txt         | 11 +++++++++++
>  .../bindings/pinctrl/qcom,msm8974-pinctrl.txt         |  6 ++++++
>  .../bindings/pinctrl/qcom,msm8994-pinctrl.txt         | 11 +++++++++++
>  .../bindings/pinctrl/qcom,msm8996-pinctrl.txt         | 11 +++++++++++
>  12 files changed, 106 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt
> index a752a4716486..7f78c6bb4e35 100644
> --- a/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt
> @@ -10,6 +10,11 @@ Required properties:
>  - #gpio-cells : Should be two.
>                  The first cell is the gpio pin number and the
>                  second cell is used for optional parameters.
> +- gpio-ranges: Range of pins managed by the GPIO controller.

Just 'see gpio.txt' is sufficient unless you can say how many entries.

> +
> +Optional properties:
> +
> +- gpio-reserved-ranges: Range of pins reserved by the TrustZone TEE.

ditto.

>  
>  Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
>  a general description of GPIO and interrupt bindings.
> @@ -67,6 +72,7 @@ Example:
>  
>  		pinctrl-names = "default";
>  		pinctrl-0 = <&gsbi5_uart_default>;
> +		gpio-ranges = <&msmgpio 0 0 90>;
>  
>  		gsbi5_uart_default: gsbi5_uart_default {
>  			mux {
> diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt
> index c4ea61ac56f2..362f32b945af 100644
> --- a/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt
> @@ -40,6 +40,16 @@ MSM8960 platform.
>  	Definition: must be 2. Specifying the pin number and flags, as defined
>  		    in <dt-bindings/gpio/gpio.h>
>  
> +- gpio-ranges:
> +	Usage: required
> +	Value type: <prop-encoded-array>

But this is phandle with 3 cells.

Still, you don't need to redefine standard properties here. Just need to 
know optional vs. required and any constraints you can define (e.g. 
allowed values, number of values, etc.)


Same comments apply to the rest.

Rob

^ permalink raw reply

* Re: [PATCH 8/8] arm64: tegra: Mark tcu as primary serial port on Tegra194 P2888
From: Jon Hunter @ 2018-05-22 16:29 UTC (permalink / raw)
  To: Mikko Perttunen, robh+dt, mark.rutland, jassisinghbrar, gregkh,
	thierry.reding
  Cc: araza, devicetree, linux-serial, linux-tegra, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20180508114403.14499-9-mperttunen@nvidia.com>


On 08/05/18 12:44, Mikko Perttunen wrote:
> The Tegra Combined UART is the proper primary serial port on P2888,
> so use it.
> 
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
>   arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi b/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi
> index 859ab5af17c1..95e2433984f7 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra194-p2888.dtsi
> @@ -10,7 +10,7 @@
>   	aliases {
>   		sdhci0 = "/cbb/sdhci@3460000";
>   		sdhci1 = "/cbb/sdhci@3400000";
> -		serial0 = &uartb;
> +		serial0 = &tcu;
>   		i2c0 = "/bpmp/i2c";
>   		i2c1 = "/cbb/i2c@3160000";
>   		i2c2 = "/cbb/i2c@c240000";
> 

Acked-by: Jon Hunter <jonathanh@nvidia.com>

Cheers
Jon

-- 
nvpublic

^ permalink raw reply

* Re: [PATCH 7/8] arm64: tegra: Add nodes for tcu on Tegra194
From: Jon Hunter @ 2018-05-22 16:28 UTC (permalink / raw)
  To: Mikko Perttunen, robh+dt, mark.rutland, jassisinghbrar, gregkh,
	thierry.reding
  Cc: araza, devicetree, linux-serial, linux-tegra, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20180508114403.14499-8-mperttunen@nvidia.com>


On 08/05/18 12:44, Mikko Perttunen wrote:
> Add nodes required for communication through the Tegra Combined UART.
> This includes the AON HSP instance, addition of shared interrupts
> for the TOP0 HSP instance, and finally the TCU node itself. Also
> mark the HSP instances as compatible to tegra194-hsp, as the hardware
> is not identical but is compatible to tegra186-hsp.
> 
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
>   arch/arm64/boot/dts/nvidia/tegra194.dtsi | 34 +++++++++++++++++++++++++++++---
>   1 file changed, 31 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
> index 6d699815a84f..d7f780b06fe2 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi
> @@ -217,10 +217,31 @@
>   		};
>   
>   		hsp_top0: hsp@3c00000 {
> -			compatible = "nvidia,tegra186-hsp";
> +			compatible = "nvidia,tegra194-hsp", "nvidia,tegra186-hsp";

I might be wrong, but I think we may need to update the binding doc 
compatibility property for hsp. For example see 
'Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-flowctrl.txt'.

Cheers
Jon

-- 
nvpublic

^ permalink raw reply

* Re: [PATCH 5/8] mailbox: tegra-hsp: Add support for shared mailboxes
From: Jon Hunter @ 2018-05-22 16:20 UTC (permalink / raw)
  To: Mikko Perttunen, robh+dt, mark.rutland, jassisinghbrar, gregkh,
	thierry.reding
  Cc: araza, devicetree, linux-serial, linux-tegra, linux-arm-kernel,
	linux-kernel
In-Reply-To: <20180508114403.14499-6-mperttunen@nvidia.com>


On 08/05/18 12:44, Mikko Perttunen wrote:
> The Tegra HSP block supports 'shared mailboxes' that are simple 32-bit
> registers consisting of a FULL bit in MSB position and 31 bits of data.
> The hardware can be configured to trigger interrupts when a mailbox
> is empty or full. Add support for these shared mailboxes to the HSP
> driver.
> 
> The initial use for the mailboxes is the Tegra Combined UART. For this
> purpose, we use interrupts to receive data, and spinning to wait for
> the transmit mailbox to be emptied to minimize unnecessary overhead.
> 
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
>   drivers/mailbox/tegra-hsp.c | 216 +++++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 193 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
> index 16eb970f2c9f..77bc8ed7ef15 100644
> --- a/drivers/mailbox/tegra-hsp.c
> +++ b/drivers/mailbox/tegra-hsp.c
> @@ -21,6 +21,11 @@
>   
>   #include <dt-bindings/mailbox/tegra186-hsp.h>
>   
> +#include "mailbox.h"
> +
> +#define HSP_INT0_IE		0x100
> +#define HSP_INT_IR		0x304
> +
>   #define HSP_INT_DIMENSIONING	0x380
>   #define HSP_nSM_SHIFT		0
>   #define HSP_nSS_SHIFT		4
> @@ -34,6 +39,8 @@
>   #define HSP_DB_RAW	0x8
>   #define HSP_DB_PENDING	0xc
>   
> +#define HSP_SM_SHRD_MBOX	0x0
> +
>   #define HSP_DB_CCPLEX		1
>   #define HSP_DB_BPMP		3
>   #define HSP_DB_MAX		7
> @@ -68,6 +75,18 @@ struct tegra_hsp_db_map {
>   	unsigned int index;
>   };
>   
> +struct tegra_hsp_mailbox {
> +	struct tegra_hsp_channel channel;
> +	unsigned int index;
> +	bool sending;
> +};
> +
> +static inline struct tegra_hsp_mailbox *
> +channel_to_mailbox(struct tegra_hsp_channel *channel)
> +{
> +	return container_of(channel, struct tegra_hsp_mailbox, channel);
> +}
> +
>   struct tegra_hsp_soc {
>   	const struct tegra_hsp_db_map *map;
>   };
> @@ -77,6 +96,7 @@ struct tegra_hsp {
>   	struct mbox_controller mbox;
>   	void __iomem *regs;
>   	unsigned int doorbell_irq;
> +	unsigned int shared_irq;
>   	unsigned int num_sm;
>   	unsigned int num_as;
>   	unsigned int num_ss;
> @@ -85,6 +105,7 @@ struct tegra_hsp {
>   	spinlock_t lock;
>   
>   	struct list_head doorbells;
> +	struct tegra_hsp_mailbox *mailboxes;
>   };
>   
>   static inline struct tegra_hsp *
> @@ -189,6 +210,35 @@ static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
>   	return IRQ_HANDLED;
>   }
>   
> +static irqreturn_t tegra_hsp_shared_irq(int irq, void *data)
> +{
> +	struct tegra_hsp_mailbox *mb;
> +	struct tegra_hsp *hsp = data;
> +	unsigned long bit, mask;
> +	u32 value;
> +
> +	mask = tegra_hsp_readl(hsp, HSP_INT_IR);
> +	/* Only interested in FULL interrupts */
> +	mask &= 0xff << 8;

Maybe add some definitions for the above.

Should we qualify 'mask' against the HSP_INT_IE register as well?

> +
> +	for_each_set_bit(bit, &mask, 16) {
> +		unsigned int mb_i = bit % 8;

If you right-shifted the mask above, you could avoid this modulo.

> +
> +		mb = &hsp->mailboxes[mb_i];
> +
> +		if (!mb->sending) {
> +			value = tegra_hsp_channel_readl(&mb->channel,
> +							HSP_SM_SHRD_MBOX);
> +			value &= ~BIT(31);

Similarly a definition for bit 31 may add some clarity.

> +			mbox_chan_received_data(mb->channel.chan, &value);
> +			tegra_hsp_channel_writel(&mb->channel, value,
> +						 HSP_SM_SHRD_MBOX);
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
>   static struct tegra_hsp_channel *
>   tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
>   			  unsigned int master, unsigned int index)
> @@ -277,15 +327,58 @@ static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_doorbell *db)
>   	spin_unlock_irqrestore(&hsp->lock, flags);
>   }
>   
> +static int tegra_hsp_mailbox_startup(struct tegra_hsp_mailbox *mb)
> +{
> +	struct tegra_hsp *hsp = mb->channel.hsp;
> +	u32 value;
> +
> +	mb->channel.chan->txdone_method = TXDONE_BY_BLOCK;
> +
> +	/* Route FULL interrupt to external IRQ 0 */
> +	value = tegra_hsp_readl(hsp, HSP_INT0_IE);
> +	value |= BIT(mb->index + 8);
> +	tegra_hsp_writel(hsp, value, HSP_INT0_IE);
> +
> +	return 0;
> +}
> +
> +static int tegra_hsp_mailbox_shutdown(struct tegra_hsp_mailbox *mb)
> +{
> +	struct tegra_hsp *hsp = mb->channel.hsp;
> +	u32 value;
> +
> +	value = tegra_hsp_readl(hsp, HSP_INT0_IE);
> +	value &= ~BIT(mb->index + 8);
> +	tegra_hsp_writel(hsp, value, HSP_INT0_IE);
> +
> +	return 0;
> +}
> +
>   static int tegra_hsp_send_data(struct mbox_chan *chan, void *data)
>   {
>   	struct tegra_hsp_channel *channel = chan->con_priv;
> -	struct tegra_hsp_doorbell *db;
> +	struct tegra_hsp_mailbox *mailbox;
> +	uint32_t value;
>   
>   	switch (channel->type) {
>   	case TEGRA_HSP_MBOX_TYPE_DB:
> -		db = channel_to_doorbell(channel);
> -		tegra_hsp_channel_writel(&db->channel, 1, HSP_DB_TRIGGER);
> +		tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> +		return 0;
> +	case TEGRA_HSP_MBOX_TYPE_SM:
> +		mailbox = channel_to_mailbox(channel);
> +		mailbox->sending = true;
> +
> +		value = *(uint32_t *)data;
> +		/* Mark mailbox full */
> +		value |= BIT(31);
> +
> +		tegra_hsp_channel_writel(channel, value, HSP_SM_SHRD_MBOX);
> +
> +		while (tegra_hsp_channel_readl(channel, HSP_SM_SHRD_MBOX)
> +		               & BIT(31))
> +			cpu_relax();

Could be nice to use readx_poll_timeout() here.

> +
> +		return 0;
>   	}
>   
>   	return -EINVAL;
> @@ -298,6 +391,8 @@ static int tegra_hsp_startup(struct mbox_chan *chan)
>   	switch (channel->type) {
>   	case TEGRA_HSP_MBOX_TYPE_DB:
>   		return tegra_hsp_doorbell_startup(channel_to_doorbell(channel));
> +	case TEGRA_HSP_MBOX_TYPE_SM:
> +		return tegra_hsp_mailbox_startup(channel_to_mailbox(channel));
>   	}
>   
>   	return -EINVAL;
> @@ -311,6 +406,9 @@ static void tegra_hsp_shutdown(struct mbox_chan *chan)
>   	case TEGRA_HSP_MBOX_TYPE_DB:
>   		tegra_hsp_doorbell_shutdown(channel_to_doorbell(channel));
>   		break;
> +	case TEGRA_HSP_MBOX_TYPE_SM:
> +		tegra_hsp_mailbox_shutdown(channel_to_mailbox(channel));
> +		break;
>   	}
>   }
>   
> @@ -364,7 +462,16 @@ static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
>   
>   	switch (type) {
>   	case TEGRA_HSP_MBOX_TYPE_DB:
> -		return tegra_hsp_doorbell_xlate(hsp, param);
> +		if (hsp->doorbell_irq)
> +			return tegra_hsp_doorbell_xlate(hsp, param);
> +		else
> +			return ERR_PTR(-EINVAL);
> +
> +	case TEGRA_HSP_MBOX_TYPE_SM:
> +		if (hsp->shared_irq && param < hsp->num_sm)
> +			return hsp->mailboxes[param].channel.chan;
> +		else
> +			return ERR_PTR(-EINVAL);
>   
>   	default:
>   		return ERR_PTR(-EINVAL);
> @@ -403,6 +510,31 @@ static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
>   	return 0;
>   }
>   
> +static int tegra_hsp_add_mailboxes(struct tegra_hsp *hsp, struct device *dev)
> +{
> +	int i;
> +
> +	hsp->mailboxes = devm_kcalloc(dev, hsp->num_sm, sizeof(*hsp->mailboxes),
> +				      GFP_KERNEL);
> +	if (!hsp->mailboxes)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < hsp->num_sm; i++) {
> +		struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i];
> +
> +		mb->index = i;
> +		mb->sending = false;
> +
> +		mb->channel.hsp = hsp;
> +		mb->channel.type = TEGRA_HSP_MBOX_TYPE_SM;
> +		mb->channel.regs = hsp->regs + SZ_64K + i * SZ_32K;
> +		mb->channel.chan = &hsp->mbox.chans[i];
> +		mb->channel.chan->con_priv = &mb->channel;
> +	}
> +
> +	return 0;
> +}
> +
>   static int tegra_hsp_probe(struct platform_device *pdev)
>   {
>   	struct tegra_hsp *hsp;
> @@ -431,14 +563,19 @@ static int tegra_hsp_probe(struct platform_device *pdev)
>   	hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
>   
>   	err = platform_get_irq_byname(pdev, "doorbell");
> -	if (err < 0) {
> -		dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
> -		return err;
> -	}
> +	if (err < 0)
> +		hsp->doorbell_irq = 0;

It should not be necessary to set to zero as it should already be zero.

> +	else
> +		hsp->doorbell_irq = err;
>   
> -	hsp->doorbell_irq = err;
> +	err = platform_get_irq_byname(pdev, "shared0");
> +	if (err < 0)
> +		hsp->shared_irq = 0;

It should not be necessary to set to zero as it should already be zero.

> +	else
> +		hsp->shared_irq = err;
>   
>   	hsp->mbox.of_xlate = of_tegra_hsp_xlate;
> +	/* First nSM are reserved for mailboxes */
>   	hsp->mbox.num_chans = 32;
>   	hsp->mbox.dev = &pdev->dev;
>   	hsp->mbox.txdone_irq = false;
> @@ -451,10 +588,22 @@ static int tegra_hsp_probe(struct platform_device *pdev)
>   	if (!hsp->mbox.chans)
>   		return -ENOMEM;
>   
> -	err = tegra_hsp_add_doorbells(hsp);
> -	if (err < 0) {
> -		dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
> -		return err;
> +	if (hsp->doorbell_irq) {
> +		err = tegra_hsp_add_doorbells(hsp);
> +		if (err < 0) {
> +			dev_err(&pdev->dev, "failed to add doorbells: %d\n",
> +			        err);
> +			return err;
> +		}
> +	}
> +
> +	if (hsp->shared_irq) {
> +		err = tegra_hsp_add_mailboxes(hsp, &pdev->dev);
> +		if (err < 0) {
> +			dev_err(&pdev->dev, "failed to add mailboxes: %d\n",
> +			        err);
> +			goto remove_doorbells;
> +		}
>   	}
>   
>   	platform_set_drvdata(pdev, hsp);
> @@ -462,20 +611,40 @@ static int tegra_hsp_probe(struct platform_device *pdev)
>   	err = mbox_controller_register(&hsp->mbox);
>   	if (err) {
>   		dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
> -		tegra_hsp_remove_doorbells(hsp);
> -		return err;
> +		goto remove_doorbells;
>   	}
>   
> -	err = devm_request_irq(&pdev->dev, hsp->doorbell_irq,
> -			       tegra_hsp_doorbell_irq, IRQF_NO_SUSPEND,
> -			       dev_name(&pdev->dev), hsp);
> -	if (err < 0) {
> -		dev_err(&pdev->dev, "failed to request doorbell IRQ#%u: %d\n",
> -			hsp->doorbell_irq, err);
> -		return err;
> +	if (hsp->doorbell_irq) {
> +		err = devm_request_irq(&pdev->dev, hsp->doorbell_irq,
> +				       tegra_hsp_doorbell_irq, IRQF_NO_SUSPEND,
> +				       dev_name(&pdev->dev), hsp);
> +		if (err < 0) {
> +			dev_err(&pdev->dev,
> +			        "failed to request doorbell IRQ#%u: %d\n",
> +				hsp->doorbell_irq, err);
> +			return err;

Clean-up?

> +		}
> +	}
> +
> +	if (hsp->shared_irq) {
> +		err = devm_request_irq(&pdev->dev, hsp->shared_irq,
> +				       tegra_hsp_shared_irq, 0,
> +				       dev_name(&pdev->dev), hsp);
> +		if (err < 0) {
> +			dev_err(&pdev->dev,
> +				"failed to request shared0 IRQ%u: %d\n",
> +				hsp->shared_irq, err);
> +			return err;

Clean-up?

> +		}
>   	}
>   
>   	return 0;
> +
> +remove_doorbells:
> +	if (hsp->doorbell_irq)
> +		tegra_hsp_remove_doorbells(hsp);
> +
> +	return err;
>   }
>   
>   static int tegra_hsp_remove(struct platform_device *pdev)
> @@ -483,7 +652,8 @@ static int tegra_hsp_remove(struct platform_device *pdev)
>   	struct tegra_hsp *hsp = platform_get_drvdata(pdev);
>   
>   	mbox_controller_unregister(&hsp->mbox);
> -	tegra_hsp_remove_doorbells(hsp);
> +	if (hsp->doorbell_irq)
> +		tegra_hsp_remove_doorbells(hsp);
>   
	>   	return 0;
>   }
> 

Cheers
Jon

-- 
nvpublic

^ permalink raw reply

* Re: [PATCH v5 1/8] dt-bindings: reset: Add AOSS reset bindings for SDM845 SoCs
From: Rob Herring @ 2018-05-22 16:17 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: bjorn.andersson, p.zabel, linux-remoteproc, linux-kernel,
	devicetree, georgi.djakov, jassisinghbrar, ohad, mark.rutland,
	kyan, sricharan, akdwived, linux-arm-msm, tsoni
In-Reply-To: <20180521172714.8551-2-sibis@codeaurora.org>

On Mon, May 21, 2018 at 10:57:07PM +0530, Sibi Sankar wrote:
> Add SDM845 AOSS (always on subsystem) reset controller binding
> 
> Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
> ---
>  .../bindings/reset/qcom,aoss-reset.txt        | 52 +++++++++++++++++++
>  include/dt-bindings/reset/qcom,sdm845-aoss.h  | 17 ++++++
>  2 files changed, 69 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/reset/qcom,aoss-reset.txt
>  create mode 100644 include/dt-bindings/reset/qcom,sdm845-aoss.h

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply

* Re: [PATCH v6 07/13] media: dt-bindings: add bindings for i.MX7 media driver
From: Rob Herring @ 2018-05-22 16:10 UTC (permalink / raw)
  To: Rui Miguel Silva
  Cc: devel, devicetree, sakari.ailus, Greg Kroah-Hartman, Ryan Harkin,
	Philipp Zabel, Steve Longerbeam, Fabio Estevam, mchehab,
	Shawn Guo, linux-clk, linux-media
In-Reply-To: <20180522145245.3143-8-rui.silva@linaro.org>

On Tue, May 22, 2018 at 03:52:39PM +0100, Rui Miguel Silva wrote:
> Add bindings documentation for i.MX7 media drivers.
> The imx7 MIPI CSI2 and imx7 CMOS Sensor Interface.
> 
> Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
> ---
>  .../devicetree/bindings/media/imx7-csi.txt    | 44 ++++++++++
>  .../bindings/media/imx7-mipi-csi2.txt         | 82 +++++++++++++++++++
>  2 files changed, 126 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/imx7-csi.txt
>  create mode 100644 Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox