From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B5C3142E6F for ; Tue, 26 Mar 2024 15:41:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711467717; cv=none; b=VMjvTZTUH20cEwG2+O6wTtufFWre6k4haa2Pz8pwdA58hP4yWHUaJpDnwtJn10ZDLdXnvVbGshElgbyI56Sjmf9mSE8fMoJtGAcJm6S+arFeCgx2Kk6QJRBQmlb1fjxJjNdesbfSj+bEI4dNhIGmox2wG9xQZWmXeJLxw++JkVM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711467717; c=relaxed/simple; bh=7u7DvkuDsYOtISFg44La36kOUlp2VxobPyDAHCLS5sA=; h=References:From:To:Cc:Subject:Date:In-reply-to:Message-ID: MIME-Version:Content-Type; b=o1wWhA8RtBOv900IRUcZAKTRnfz6hKoUhPVYOD5TAhkjanWVvCPsUCzezbUnnZn6mWFL+8B1m5VnMzyPx5ulLIQqcqkd8Z2/PR0hGk2LCDEVZDrJx+5FixgfsU2WgHKRuDgF+fBdZ2Y+0A8VRUkyasUk6J7RHkv2097f0Fv9OKo= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=c7Db+rPw; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="c7Db+rPw" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4148c5de23fso9056275e9.1 for ; Tue, 26 Mar 2024 08:41:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1711467713; x=1712072513; darn=vger.kernel.org; h=mime-version:message-id:in-reply-to:date:subject:cc:to:from :user-agent:references:from:to:cc:subject:date:message-id:reply-to; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=c7Db+rPwjvrkvd/L2wmiaury0jsOOTkS6WlnnVUFCUEkDeQ9vxPN66GdzJmxKSFCLN kfYrTp2VBShjCgmR/xLoyeG8aT/4cZ3SfAiVTQYAbOCAhN+ZqNegJ5hDLPshG4zOkncV +OujLJs9JFjCRDmSfuB4ko5zJQFeJmI4zU87CEWfrk4MqGbQ7NqRi/YarVdxn5Q9ZS2C WbMG1E97U4nvKNO9mv29+q/bmfIZLJeEppKmfctzXeKuhafDMZBJ0JWfK3sgimrUl2tt KoufzP7A2QwGpa4szh3upydIpRnvsXsKDoFro34DRjAAEUBnTZ13kp14FOoNXbPwiB+t P4ow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711467713; x=1712072513; h=mime-version:message-id:in-reply-to:date:subject:cc:to:from :user-agent:references:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=gybdOIn27KWTyPHnwmAeCl8dqBpYYzyycIjuThoJqBv/h9BvYopi1hWVp/gsEEcbMa QWkiU0SHiIrbvMMaet+LYBnyeAnqsL+yGomS88hU39M7zRcXz2w/stKbQVoM2p92PptT MLBYyQ19rT4ikv6PZtmN2R0tx6Q0lleLJpgzdD9UCTYh3Ux6v4XeXflx+XmgIbB6OPkv dvFVn8PSC/Kdt4YNdE5Ie64Ybpet3O6Lqr+1p2MFI3KNLd106o5GJ4SDN9w9gUO5Nspc jkyha/XRnyVNS9Pe56Ji82yRXAvU0w+VVF1F5NX+mC6LixPqtaEvaJ7IzXYuLNwjgsrn sRGw== X-Forwarded-Encrypted: i=1; AJvYcCWkxHrxhFyq9Ah3/PgFVTHnyHFsxlDHjIX5aOCmfkYjZXpfScCncP76d22i/LdzELEkjZQ/UhYQyGieEAFa0p8Jtw1Ao1fcowu55w== X-Gm-Message-State: AOJu0Yyyb3OpcliRIJ3S68yVYh5jgeWN9fWOEFT0qD8ELa0ka3XGz+h4 3CRd4aQQj+/NcYQwbucN3ck2Ublq+RE7rFWS3JSi3iKVggy/Qz4TPFLmVDU3iJ8= X-Google-Smtp-Source: AGHT+IH7q/cpz8ILaErtLAXwphlwM1LTCLYtF6V5eMpQldTUIrcdWn3kgNQyW/tgQYAiJ+q7VBw8dw== X-Received: by 2002:a7b:cd08:0:b0:413:ea5c:1b08 with SMTP id f8-20020a7bcd08000000b00413ea5c1b08mr6400986wmj.40.1711467712539; Tue, 26 Mar 2024 08:41:52 -0700 (PDT) Received: from localhost ([2a01:e0a:3c5:5fb1:97a2:bb9f:463a:6468]) by smtp.gmail.com with ESMTPSA id l21-20020a05600c4f1500b004148ab95c36sm5397116wmq.41.2024.03.26.08.41.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Mar 2024 08:41:52 -0700 (PDT) References: <20240314232201.2102178-1-jan.dakinevich@salutedevices.com> <20240314232201.2102178-5-jan.dakinevich@salutedevices.com> <1j4jd7izx4.fsf@starbuckisacylon.baylibre.com> <6c129ee2-f08c-4575-a95f-667cc6472578@salutedevices.com> <1jo7bafv90.fsf@starbuckisacylon.baylibre.com> <0f4b2ec3-204f-44ea-a1b2-e47ea05def1f@salutedevices.com> User-agent: mu4e 1.10.8; emacs 29.2 From: Jerome Brunet To: Jan Dakinevich Cc: Jerome Brunet , Neil Armstrong , Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Kevin Hilman , Martin Blumenstingl , Liam Girdwood , Mark Brown , Linus Walleij , Jaroslav Kysela , Takashi Iwai , linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org, kernel@salutedevices.com Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver Date: Tue, 26 Mar 2024 16:26:52 +0100 In-reply-to: <0f4b2ec3-204f-44ea-a1b2-e47ea05def1f@salutedevices.com> Message-ID: <1jy1a580g0.fsf@starbuckisacylon.baylibre.com> Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain On Sat 23 Mar 2024 at 21:02, Jan Dakinevich wrote: > Jerome, I have reworked my driver reusing axg-audio code as most as I > could and now I have one more question. Lets see on this definition from > axg-audio: > > #define AUD_MST_MUX(_name, _reg, _flag) \ > AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \ > mst_mux_parent_data, 0) > > #define AUD_MST_MCLK_MUX(_name, _reg) \ > AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST) > > CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll > clock will not be reconfigured at runtime to satisfy the rate that was > requested from axg-tdm. > Yes, that is by design. It is another area where mainline audio differs greatly from AML vendor code. The PLLs are expected be to fixed and the audio master clock will reparent to the most adequate PLL source depending on the use case. This is how we manage to satisfy all audio interfaces with a very limited number of PLLs On AXG/G12 there is at most 6 concurrent interfaces (3 FRDDR/TODDR) - 8 on sm1 - and we can satisfy on that with 3 PLLs. That would not be possible if interfaces were having their way with the PLLs, reseting it everytime a stream is started. The PLL rate should be carefully chosen so it can be derived easily. On AXG/G12/SM1 that is: * one PLL per rate family, to maximize clock precision * x24 x32: to handle different sample sizes * x2 until we reach the PLL limits to allow higher rates such as 384kHz or even higher If you have less PLLs on A1, you'll have to make compromises, like a less precise clock to support multiple family with one PLL. This is why the PLLs are set for each platform in DT because that choice may depend on the platform use case. > > On 3/19/24 11:30, Jerome Brunet wrote: >> >> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich wrote: >> >>> Let's start from the end: >>> >>>> No - Looks to me you just have two clock controllers you are trying >>> force into one. >>> >>>> Again, this shows 2 devices. The one related to your 'map0' should >>> request AUD2_CLKID_AUDIOTOP as input and enable it right away. >>> >>> Most of fishy workarounds that you commented is caused the fact the mmio >>> of this clock controller is divided into two parts. Compare it with >>> axg-audio driver, things that was part of contigous memory region (like >>> pdm) here are moved to second region. Is this enough to make a guess >>> that these are two devices? >> >> I see obsolutely no reason to think it is a single device nor to add all the quirks >> you have the way you did. So yes, in that case, 2 zones, 2 devices. >> >>> >>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be >>> enabled before enabling of clocks from second region too. That is >>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller. >>> >> >> Yes. I understood the first time around and already commented on that. >> >>> >>> On 3/15/24 12:20, Jerome Brunet wrote: >>>> >>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich wrote: >>>> >>>>> This controller provides clocks and reset functionality for audio >>>>> peripherals on Amlogic A1 SoC family. >>>>> >>>>> The driver is almost identical to 'axg-audio', however it would be better >>>>> to keep it separate due to following reasons: >>>>> >>>>> - significant amount of bits has another definition. I will bring there >>>>> a mess of new defines with A1_ suffixes. >>>>> >>>>> - registers of this controller are located in two separate regions. It >>>>> will give a lot of complications for 'axg-audio' to support this. >>>>> >>>>> Signed-off-by: Jan Dakinevich >>>>> --- >>>>> drivers/clk/meson/Kconfig | 13 + >>>>> drivers/clk/meson/Makefile | 1 + >>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++ >>>>> drivers/clk/meson/a1-audio.h | 58 ++++ >>>>> 4 files changed, 628 insertions(+) >>>>> create mode 100644 drivers/clk/meson/a1-audio.c >>>>> create mode 100644 drivers/clk/meson/a1-audio.h >>>>> >>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig >>>>> index d6a2fa5f7e88..80c4a18c83d2 100644 >>>>> --- a/drivers/clk/meson/Kconfig >>>>> +++ b/drivers/clk/meson/Kconfig >>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS >>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock >>>>> controller to work. >>>>> >>>>> +config COMMON_CLK_A1_AUDIO >>>>> + tristate "Amlogic A1 SoC Audio clock controller support" >>>>> + depends on ARM64 >>>>> + select COMMON_CLK_MESON_REGMAP >>>>> + select COMMON_CLK_MESON_CLKC_UTILS >>>>> + select COMMON_CLK_MESON_PHASE >>>>> + select COMMON_CLK_MESON_SCLK_DIV >>>>> + select COMMON_CLK_MESON_AUDIO_RSTC >>>>> + help >>>>> + Support for the Audio clock controller on Amlogic A113L based >>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller >>>>> + to work. >>>>> + >>>>> config COMMON_CLK_G12A >>>>> tristate "G12 and SM1 SoC clock controllers support" >>>>> depends on ARM64 >>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile >>>>> index 88d94921a4dc..4968fc7ad555 100644 >>>>> --- a/drivers/clk/meson/Makefile >>>>> +++ b/drivers/clk/meson/Makefile >>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o >>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o >>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o >>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o >>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o >>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c >>>>> new file mode 100644 >>>>> index 000000000000..6039116c93ba >>>>> --- /dev/null >>>>> +++ b/drivers/clk/meson/a1-audio.c >>>>> @@ -0,0 +1,556 @@ >>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT) >>>>> +/* >>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. >>>>> + * >>>>> + * Author: Jan Dakinevich >>>>> + */ >>>>> + >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> + >>>>> +#include "meson-clkc-utils.h" >>>>> +#include "meson-audio-rstc.h" >>>>> +#include "clk-regmap.h" >>>>> +#include "clk-phase.h" >>>>> +#include "sclk-div.h" >>>>> +#include "a1-audio.h" >>>>> + >>>>> +#define AUDIO_PDATA(_name) \ >>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } }) >>>> >>>> Not a fan - yet another level of macro. >>>> >>>>> + >>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_mux_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .mask = (_mask), \ >>>>> + .shift = (_shift), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_mux_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = ARRAY_SIZE(_pdata), \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_div_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_divider_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_gate_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .bit_idx = (_bit), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_gate_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \ >>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_sclk_div_data) { \ >>>>> + .div = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_div_shift), \ >>>>> + .width = (_div_width), \ >>>>> + }, \ >>>>> + .hi = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_hi_shift), \ >>>>> + .width = (_hi_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_sclk_div_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \ >>>> >>>> Does not help readeability. Just pass the flag as axg-audio does. >>>> >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \ >>>>> + _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_clk_triphase_data) { \ >>>>> + .ph0 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift0), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ph1 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift1), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ph2 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift2), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_clk_triphase_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \ >>>>> + _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_sclk_ws_inv_data) { \ >>>>> + .ph = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift_ph), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ws = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift_ws), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_sclk_ws_inv_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \ >>>>> + }, \ >>>>> +} >>>> >>>> All the above does essentially the same things as the macro of >>>> axg-audio, to some minor differences. Yet it is another set to maintain. >>>> >>> >>> Except one thing... Here I keep memory identifier to which this clock >>> belongs: >>> >>> .map = AUDIO_REG_MAP(_reg), >>> >>> It is workaround, but ->map the only common field in clk_regmap that >>> could be used for this purpose. >>> >>> >>>> I'd much prefer if you put the axg-audio macro in a header a re-used >>>> those. There would a single set to maintain. You may then specialize the >>>> included in the driver C file, to avoid redundant parameters >>>> >>>> Rework axg-audio to use clk_parent_data if you must, but not in the same >>>> series please. >>>> >>>>> + >>>>> +static const struct clk_parent_data a1_pclk_pdata[] = { >>>>> + { .fw_name = "pclk", }, >>>>> +}; >>>>> + >>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata); >>>> This is what I mean by redundant parameter ^ >>>> >>> >>> Yep. I could define something like AUDIO_PCLK_GATE(). >>> >>>>> + >>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata); >>>>> + >>>>> +static const struct clk_parent_data a1_mst_pdata[] = { >>>>> + { .fw_name = "dds_in" }, >>>>> + { .fw_name = "fclk_div2" }, >>>>> + { .fw_name = "fclk_div3" }, >>>>> + { .fw_name = "hifi_pll" }, >>>>> + { .fw_name = "xtal" }, >>>>> +}; >>>>> + >>>>> +#define AUDIO_MST_MCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \ >>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \ >>>>> + AUDIO_PDATA(_name##_mux)); \ >>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div)) >>>>> + >>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL); >>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); >>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL); >>>>> + >>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8, >>>>> + AUDIO_PDATA(audio_resample_clk_mux)); >>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31, >>>>> + AUDIO_PDATA(audio_resample_clk_div)); >>>>> + >>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8, >>>>> + AUDIO_PDATA(audio_locker_in_clk_mux)); >>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15, >>>>> + AUDIO_PDATA(audio_locker_in_clk_div)); >>>>> + >>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8, >>>>> + AUDIO_PDATA(audio_locker_out_clk_mux)); >>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31, >>>>> + AUDIO_PDATA(audio_locker_out_clk_div)); >>>>> + >>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL); >>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL); >>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0); >>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1); >>>>> + >>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \ >>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \ >>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \ >>>>> + AUDIO_PDATA(_name##_pre_en), true); \ >>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \ >>>>> + AUDIO_PDATA(_name##_div)); \ >>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \ >>>>> + AUDIO_PDATA(_name##_post_en)) >>>>> + >>>> >>>> Again, I'm not a fan of this many levels of macro. I can live with it >>>> but certainly don't want the burden of reviewing and maintaining for >>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common. >>>> >>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \ >>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \ >>>>> + (_pdata), false); \ >>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \ >>>>> + AUDIO_PDATA(_name##_div)) >>>>> + >>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_a_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_b_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_c_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_d_mclk)); >>>>> + >>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en)); >>>>> + >>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = { >>>>> + { .hw = &audio_mst_a_sclk.hw }, >>>>> + { .hw = &audio_mst_b_sclk.hw }, >>>>> + { .hw = &audio_mst_c_sclk.hw }, >>>>> + { .hw = &audio_mst_d_sclk.hw }, >>>>> + { .fw_name = "slv_sclk0" }, >>>>> + { .fw_name = "slv_sclk1" }, >>>>> + { .fw_name = "slv_sclk2" }, >>>>> + { .fw_name = "slv_sclk3" }, >>>>> + { .fw_name = "slv_sclk4" }, >>>>> + { .fw_name = "slv_sclk5" }, >>>>> + { .fw_name = "slv_sclk6" }, >>>>> + { .fw_name = "slv_sclk7" }, >>>>> + { .fw_name = "slv_sclk8" }, >>>>> + { .fw_name = "slv_sclk9" }, >>>>> +}; >>>>> + >>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = { >>>>> + { .hw = &audio_mst_a_lrclk.hw }, >>>>> + { .hw = &audio_mst_b_lrclk.hw }, >>>>> + { .hw = &audio_mst_c_lrclk.hw }, >>>>> + { .hw = &audio_mst_d_lrclk.hw }, >>>>> + { .fw_name = "slv_lrclk0" }, >>>>> + { .fw_name = "slv_lrclk1" }, >>>>> + { .fw_name = "slv_lrclk2" }, >>>>> + { .fw_name = "slv_lrclk3" }, >>>>> + { .fw_name = "slv_lrclk4" }, >>>>> + { .fw_name = "slv_lrclk5" }, >>>>> + { .fw_name = "slv_lrclk6" }, >>>>> + { .fw_name = "slv_lrclk7" }, >>>>> + { .fw_name = "slv_lrclk8" }, >>>>> + { .fw_name = "slv_lrclk9" }, >>>>> +}; >>>>> + >>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \ >>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \ >>>>> + AUDIO_PDATA(_name##_mux)); \ >>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \ >>>>> + AUDIO_PDATA(_name##_pre_en)); \ >>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \ >>>>> + AUDIO_PDATA(_name##_post_en)) >>>>> + >>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata) >>>>> + >>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL); >>>>> + >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL); >>>>> + >>>>> +static struct clk_hw *a1_audio_hw_clks[] = { >>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw, >>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw, >>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw, >>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw, >>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw, >>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw, >>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw, >>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw, >>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw, >>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw, >>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw, >>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw, >>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw, >>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw, >>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw, >>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw, >>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw, >>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw, >>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw, >>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw, >>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw, >>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw, >>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw, >>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw, >>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw, >>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw, >>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw, >>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw, >>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw, >>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw, >>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw, >>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw, >>>>> + >>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw, >>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw, >>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw, >>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw, >>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw, >>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw, >>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw, >>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw, >>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw, >>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw, >>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw, >>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw, >>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw, >>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw, >>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw, >>>>> +}; >>>>> + >>>>> +static struct meson_clk_hw_data a1_audio_clks = { >>>>> + .hws = a1_audio_hw_clks, >>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks), >>>>> +}; >>>>> + >>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev, >>>>> + unsigned int index) >>>>> +{ >>>>> + char name[32]; >>>>> + const struct regmap_config cfg = { >>>>> + .reg_bits = 32, >>>>> + .val_bits = 32, >>>>> + .reg_stride = 4, >>>>> + .name = name, >>>> >>>> Not necessary >>>> >>> >>> This implementation uses two regmaps, and this field allow to avoid >>> errors like this: >>> >>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with >>> parent 'regmap' already present! >>> >>>>> + }; >>>>> + void __iomem *base; >>>>> + >>>>> + base = devm_platform_ioremap_resource(pdev, index); >>>>> + if (IS_ERR(base)) >>>>> + return base; >>>>> + >>>>> + scnprintf(name, sizeof(name), "%d", index); >>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg); >>>>> +} >>>> >>>> That is overengineered. Please keep it simple. Declare the regmap_config >>>> as static const global, and do it like axg-audio please. >>>> >>> >>> This only reason why it is not "static const" because I need to set >>> unique name for each regmap. >>> >>>>> + >>>>> +static int a1_register_clk(struct platform_device *pdev, >>>>> + struct regmap *map0, struct regmap *map1, >>>>> + struct clk_hw *hw) >>>>> +{ >>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw); >>>>> + >>>>> + if (!hw) >>>>> + return 0; >>>>> + >>>>> + switch ((unsigned long)clk->map) { >>>>> + case AUDIO_RANGE_0: >>>>> + clk->map = map0; >>>>> + break; >>>>> + case AUDIO_RANGE_1: >>>>> + clk->map = map1; >>>>> + break; >>>> >>>> ... fishy >>>> >>>>> + default: >>>>> + WARN_ON(1); >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + return devm_clk_hw_register(&pdev->dev, hw); >>>>> +} >>>>> + >>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev) >>>>> +{ >>>>> + struct regmap *map0, *map1; >>>>> + struct clk *clk; >>>>> + unsigned int i; >>>>> + int ret; >>>>> + >>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk"); >>>>> + if (WARN_ON(IS_ERR(clk))) >>>>> + return PTR_ERR(clk); >>>>> + >>>>> + map0 = a1_audio_map(pdev, 0); >>>>> + if (IS_ERR(map0)) >>>>> + return PTR_ERR(map0); >>>>> + >>>>> + map1 = a1_audio_map(pdev, 1); >>>>> + if (IS_ERR(map1)) >>>>> + return PTR_ERR(map1); >>>> >>>> No - Looks to me you just have two clock controllers you are trying >>>> force into one. >>>> >>> >>> See the begining. >>> >>>>> + >>>>> + /* >>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless >>>>> + * it is enabled any read/write to 'map0' hangs the CPU. >>>>> + */ >>>>> + >>>>> + ret = a1_register_clk(pdev, map0, map1, >>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk); >>>>> + if (ret) >>>>> + return ret; >>>> >>>> Again, this shows 2 devices. The one related to your 'map0' should >>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away. >>>> >>> >>> See the begining. >>> >>>>> + >>>>> + for (i = 0; i < a1_audio_clks.num; i++) { >>>>> + if (i == AUD2_CLKID_AUDIOTOP) >>>>> + continue; >>>>> + >>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]); >>>>> + if (ret) >>>>> + return ret; >>>>> + } >>>>> + >>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get, >>>>> + &a1_audio_clks); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) != >>>>> + AUDIO_RANGE_0); >>>> >>>> Why is that necessary ? >>>> >>> >>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register, >>> and I want to assert it. >>> >>>>> + return meson_audio_rstc_register(&pdev->dev, map0, >>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32); >>>>> +} >>>>> + >>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = { >>>>> + { .compatible = "amlogic,a1-audio-clkc", }, >>>>> + {} >>>>> +}; >>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table); >>>>> + >>>>> +static struct platform_driver a1_audio_clkc_driver = { >>>>> + .probe = a1_audio_clkc_probe, >>>>> + .driver = { >>>>> + .name = "a1-audio-clkc", >>>>> + .of_match_table = a1_audio_clkc_match_table, >>>>> + }, >>>>> +}; >>>>> +module_platform_driver(a1_audio_clkc_driver); >>>>> + >>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver"); >>>>> +MODULE_AUTHOR("Jan Dakinevich "); >>>>> +MODULE_LICENSE("GPL"); >>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h >>>>> new file mode 100644 >>>>> index 000000000000..f994e87276cd >>>>> --- /dev/null >>>>> +++ b/drivers/clk/meson/a1-audio.h >>>>> @@ -0,0 +1,58 @@ >>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ >>>>> +/* >>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. >>>>> + * >>>>> + * Author: Jan Dakinevich >>>>> + */ >>>>> + >>>>> +#ifndef __A1_AUDIO_H >>>>> +#define __A1_AUDIO_H >>>>> + >>>>> +#define AUDIO_RANGE_0 0xa >>>>> +#define AUDIO_RANGE_1 0xb >>>>> +#define AUDIO_RANGE_SHIFT 16 >>>>> + >>>>> +#define AUDIO_REG(_range, _offset) \ >>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset)) >>>>> + >>>>> +#define AUDIO_REG_OFFSET(_reg) \ >>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1)) >>>>> + >>>>> +#define AUDIO_REG_MAP(_reg) \ >>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT)) >>>> >>>> That is seriouly overengineered. >>>> The following are offset. Just write what they are. >>>> >>> >>> This is all in order to keep range's identifier together with offset and >>> then use it to store the identifier in clk_regmaps. >>> >>>> There is not reason to put that into a header. It is only going to be >>>> used by a single driver. >>>>>> + >>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000) >>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008) >>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c) >>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010) >>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014) >>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018) >>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c) >>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028) >>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040) >>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044) >>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048) >>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c) >>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050) >>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054) >>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058) >>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c) >>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080) >>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084) >>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c) >>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090) >>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094) >>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c) >>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4) >>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8) >>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0) >>>>> + >>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c) >>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040) >>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044) >>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058) >>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c) >>>>> + >>>>> +#include >>>>> + >>>>> +#endif /* __A1_AUDIO_H */ >>>> >>>> >> >> -- Jerome From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 213FEC6FD1F for ; Tue, 26 Mar 2024 16:49:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:In-reply-to: Date:Subject:Cc:To:From:References:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=OPIT7WsTamhASqi1seQUxBKwSMuQr4AKmhOHL3Es8qc=; b=TmSFxnCoEObrxW jhOGjbH9X4q+d5XlHE2AYham51KZSOBoFItnvaSYxVKvtk+Q5rbfj2aAOePt0VFmQA/WBffWdIvkq 00OA98bbvvWVSv3VhTPnNM1PnP/Hu0MjwbSWxy1Yt/EJu+r9LRN3zTv1pfClEJVIODmaDxkUZziUS rHSxJ2uXOnxuijQW6XOJDy42bbSFEWQ5K8BED+lrJ3NvNyMn3hKmsiA5g2L57gP4/GSH10r8liEAF 6+AWTDSAbEcrVoaxFkh/+wI48s242f8WdHeqlyXOq9sOANMOHHY8oiBdmKIJFN0wL3ZLvH2VNemX2 hXgUxkpxwG6OmijvPCSw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rp9zO-00000005Z06-0MlF; Tue, 26 Mar 2024 16:49:30 +0000 Received: from desiato.infradead.org ([2001:8b0:10b:1:d65d:64ff:fe57:4e05]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rp8w2-00000005GGt-40Qy for linux-amlogic@bombadil.infradead.org; Tue, 26 Mar 2024 15:41:59 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=Content-Type:MIME-Version:Message-ID: In-reply-to:Date:Subject:Cc:To:From:References:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=LEpJi4qpKiaqFIzwUewI7Bk/RE /jwegNPbeQC9zoJGcyLrWWTH2baBkzcXxyLhROpUwa4WYrdg9GtjTDL8xn9GTSkXe8Hvg4AkllZlw 3izePSWNmBQvqYFRElC77xlgp6jXpoULiuwSvg8g1+et2EheNGlTkPSYuiJa0hEvK1nYqy1YKprMw pCZHWser9Wfyq47jEdypD9DnBOBCHpDk9755Ljo2eIhG/SZp51b6dJ6MiwMLYensLAPMz8JjmKG7N LVXC+UxZ5rCGDrh8vBVXUHuhnV1j8xNqNSo0QQ988Yf/EUjIndz5ZKFMWxIyV50OmiRBWm1wVfEb6 Alo/s+Yg==; Received: from mail-wm1-x32b.google.com ([2a00:1450:4864:20::32b]) by desiato.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rp8vz-0000000HH8i-15I1 for linux-amlogic@lists.infradead.org; Tue, 26 Mar 2024 15:41:57 +0000 Received: by mail-wm1-x32b.google.com with SMTP id 5b1f17b1804b1-414911fa3edso1514625e9.0 for ; Tue, 26 Mar 2024 08:41:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1711467713; x=1712072513; darn=lists.infradead.org; h=mime-version:message-id:in-reply-to:date:subject:cc:to:from :user-agent:references:from:to:cc:subject:date:message-id:reply-to; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=tweF+27EfuV67DXBxos/sek1rWsxf1+leaXmjBQpAXo6MLe8KXHpKkaOGBOtzQpwF2 cA8MdREC2W/j9rN8qgyu/HRdKY8pwLewK+hM+JJoNK26aYjXMuvrxmAOjvfC0BQ6KGCe JBa0CNlxSVdkfFR0QnfRuCA1XwP7MLXD5ezaUFw+4NG+v4CybXsjT5Qc4FS2uT9jn073 5Ucyo3PAW7NwhyGoXekvWRjbu3PcVtui7VZ0fmKGUtOvWHIHnCws0hy+a3OFghGIlRX5 /MyXwYR//otK8kLTFvM6GAV4zjiVz5+ODemtQhVbnAdgETdy6lDfG1aLsOk9Ekm1KFLA dl3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711467713; x=1712072513; h=mime-version:message-id:in-reply-to:date:subject:cc:to:from :user-agent:references:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=XaGiDDTPkcJajhXJnEucTdrZOKxfEAUMqLyA/M2Rxq+ixgnRF5AQrBw56wUNFeAj5s BO2TrKFInJLn+s4iTzLPxjkZx2nLSJX5uzfOG3NTQTTGuR16hLO/KbOouwXq+i52a76M 43LuxR81ruBTOrgWhANjQnk6MQm9DBqp8KOq2/z5InOOXupVI6TwCGbeEMZMsTV3zuan Z30hIFDVddeZP0viDwQ4hfMFWjD51tMqFgiU55WjKMe37bgZemozGXnl/0kP7ZdjA+My e0+aSKd9tQy+cxH/U6R4Wfufgn0+WcgmdVKJJZjG7Vt08nYEIgQ2Kr6+C0uEzlc7+NVD Z4hw== X-Forwarded-Encrypted: i=1; AJvYcCVfLbuwXeI8DedEzf6S12u39yrHPfeJzVeyjB2duo2dOqeuZs5UYPAF7MUAp7pnz6MoYG6i4Kl+GYTElMyMvyROEvoGQGSogusHuispBf6beq8= X-Gm-Message-State: AOJu0Yw1YYa9J4HZL6GvVRCMClpimuLPNJw/zRjQXUUz9o+XYDgSa/y6 CNoZQzpTJAGXmJemrDRuL8PxkptbF9GdApzrdxYg1VuAvp7udroVCN61cct7Y0Q= X-Google-Smtp-Source: AGHT+IH7q/cpz8ILaErtLAXwphlwM1LTCLYtF6V5eMpQldTUIrcdWn3kgNQyW/tgQYAiJ+q7VBw8dw== X-Received: by 2002:a7b:cd08:0:b0:413:ea5c:1b08 with SMTP id f8-20020a7bcd08000000b00413ea5c1b08mr6400986wmj.40.1711467712539; Tue, 26 Mar 2024 08:41:52 -0700 (PDT) Received: from localhost ([2a01:e0a:3c5:5fb1:97a2:bb9f:463a:6468]) by smtp.gmail.com with ESMTPSA id l21-20020a05600c4f1500b004148ab95c36sm5397116wmq.41.2024.03.26.08.41.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Mar 2024 08:41:52 -0700 (PDT) References: <20240314232201.2102178-1-jan.dakinevich@salutedevices.com> <20240314232201.2102178-5-jan.dakinevich@salutedevices.com> <1j4jd7izx4.fsf@starbuckisacylon.baylibre.com> <6c129ee2-f08c-4575-a95f-667cc6472578@salutedevices.com> <1jo7bafv90.fsf@starbuckisacylon.baylibre.com> <0f4b2ec3-204f-44ea-a1b2-e47ea05def1f@salutedevices.com> User-agent: mu4e 1.10.8; emacs 29.2 From: Jerome Brunet To: Jan Dakinevich Cc: Jerome Brunet , Neil Armstrong , Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Kevin Hilman , Martin Blumenstingl , Liam Girdwood , Mark Brown , Linus Walleij , Jaroslav Kysela , Takashi Iwai , linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org, kernel@salutedevices.com Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver Date: Tue, 26 Mar 2024 16:26:52 +0100 In-reply-to: <0f4b2ec3-204f-44ea-a1b2-e47ea05def1f@salutedevices.com> Message-ID: <1jy1a580g0.fsf@starbuckisacylon.baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240326_154155_596739_FD44FFF0 X-CRM114-Status: GOOD ( 31.14 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org On Sat 23 Mar 2024 at 21:02, Jan Dakinevich wrote: > Jerome, I have reworked my driver reusing axg-audio code as most as I > could and now I have one more question. Lets see on this definition from > axg-audio: > > #define AUD_MST_MUX(_name, _reg, _flag) \ > AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \ > mst_mux_parent_data, 0) > > #define AUD_MST_MCLK_MUX(_name, _reg) \ > AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST) > > CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll > clock will not be reconfigured at runtime to satisfy the rate that was > requested from axg-tdm. > Yes, that is by design. It is another area where mainline audio differs greatly from AML vendor code. The PLLs are expected be to fixed and the audio master clock will reparent to the most adequate PLL source depending on the use case. This is how we manage to satisfy all audio interfaces with a very limited number of PLLs On AXG/G12 there is at most 6 concurrent interfaces (3 FRDDR/TODDR) - 8 on sm1 - and we can satisfy on that with 3 PLLs. That would not be possible if interfaces were having their way with the PLLs, reseting it everytime a stream is started. The PLL rate should be carefully chosen so it can be derived easily. On AXG/G12/SM1 that is: * one PLL per rate family, to maximize clock precision * x24 x32: to handle different sample sizes * x2 until we reach the PLL limits to allow higher rates such as 384kHz or even higher If you have less PLLs on A1, you'll have to make compromises, like a less precise clock to support multiple family with one PLL. This is why the PLLs are set for each platform in DT because that choice may depend on the platform use case. > > On 3/19/24 11:30, Jerome Brunet wrote: >> >> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich wrote: >> >>> Let's start from the end: >>> >>>> No - Looks to me you just have two clock controllers you are trying >>> force into one. >>> >>>> Again, this shows 2 devices. The one related to your 'map0' should >>> request AUD2_CLKID_AUDIOTOP as input and enable it right away. >>> >>> Most of fishy workarounds that you commented is caused the fact the mmio >>> of this clock controller is divided into two parts. Compare it with >>> axg-audio driver, things that was part of contigous memory region (like >>> pdm) here are moved to second region. Is this enough to make a guess >>> that these are two devices? >> >> I see obsolutely no reason to think it is a single device nor to add all the quirks >> you have the way you did. So yes, in that case, 2 zones, 2 devices. >> >>> >>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be >>> enabled before enabling of clocks from second region too. That is >>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller. >>> >> >> Yes. I understood the first time around and already commented on that. >> >>> >>> On 3/15/24 12:20, Jerome Brunet wrote: >>>> >>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich wrote: >>>> >>>>> This controller provides clocks and reset functionality for audio >>>>> peripherals on Amlogic A1 SoC family. >>>>> >>>>> The driver is almost identical to 'axg-audio', however it would be better >>>>> to keep it separate due to following reasons: >>>>> >>>>> - significant amount of bits has another definition. I will bring there >>>>> a mess of new defines with A1_ suffixes. >>>>> >>>>> - registers of this controller are located in two separate regions. It >>>>> will give a lot of complications for 'axg-audio' to support this. >>>>> >>>>> Signed-off-by: Jan Dakinevich >>>>> --- >>>>> drivers/clk/meson/Kconfig | 13 + >>>>> drivers/clk/meson/Makefile | 1 + >>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++ >>>>> drivers/clk/meson/a1-audio.h | 58 ++++ >>>>> 4 files changed, 628 insertions(+) >>>>> create mode 100644 drivers/clk/meson/a1-audio.c >>>>> create mode 100644 drivers/clk/meson/a1-audio.h >>>>> >>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig >>>>> index d6a2fa5f7e88..80c4a18c83d2 100644 >>>>> --- a/drivers/clk/meson/Kconfig >>>>> +++ b/drivers/clk/meson/Kconfig >>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS >>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock >>>>> controller to work. >>>>> >>>>> +config COMMON_CLK_A1_AUDIO >>>>> + tristate "Amlogic A1 SoC Audio clock controller support" >>>>> + depends on ARM64 >>>>> + select COMMON_CLK_MESON_REGMAP >>>>> + select COMMON_CLK_MESON_CLKC_UTILS >>>>> + select COMMON_CLK_MESON_PHASE >>>>> + select COMMON_CLK_MESON_SCLK_DIV >>>>> + select COMMON_CLK_MESON_AUDIO_RSTC >>>>> + help >>>>> + Support for the Audio clock controller on Amlogic A113L based >>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller >>>>> + to work. >>>>> + >>>>> config COMMON_CLK_G12A >>>>> tristate "G12 and SM1 SoC clock controllers support" >>>>> depends on ARM64 >>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile >>>>> index 88d94921a4dc..4968fc7ad555 100644 >>>>> --- a/drivers/clk/meson/Makefile >>>>> +++ b/drivers/clk/meson/Makefile >>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o >>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o >>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o >>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o >>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o >>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c >>>>> new file mode 100644 >>>>> index 000000000000..6039116c93ba >>>>> --- /dev/null >>>>> +++ b/drivers/clk/meson/a1-audio.c >>>>> @@ -0,0 +1,556 @@ >>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT) >>>>> +/* >>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. >>>>> + * >>>>> + * Author: Jan Dakinevich >>>>> + */ >>>>> + >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> + >>>>> +#include "meson-clkc-utils.h" >>>>> +#include "meson-audio-rstc.h" >>>>> +#include "clk-regmap.h" >>>>> +#include "clk-phase.h" >>>>> +#include "sclk-div.h" >>>>> +#include "a1-audio.h" >>>>> + >>>>> +#define AUDIO_PDATA(_name) \ >>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } }) >>>> >>>> Not a fan - yet another level of macro. >>>> >>>>> + >>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_mux_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .mask = (_mask), \ >>>>> + .shift = (_shift), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_mux_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = ARRAY_SIZE(_pdata), \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_div_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_divider_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_gate_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .bit_idx = (_bit), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_gate_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \ >>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_sclk_div_data) { \ >>>>> + .div = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_div_shift), \ >>>>> + .width = (_div_width), \ >>>>> + }, \ >>>>> + .hi = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_hi_shift), \ >>>>> + .width = (_hi_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_sclk_div_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \ >>>> >>>> Does not help readeability. Just pass the flag as axg-audio does. >>>> >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \ >>>>> + _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_clk_triphase_data) { \ >>>>> + .ph0 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift0), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ph1 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift1), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ph2 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift2), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_clk_triphase_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \ >>>>> + _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_sclk_ws_inv_data) { \ >>>>> + .ph = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift_ph), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ws = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift_ws), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_sclk_ws_inv_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \ >>>>> + }, \ >>>>> +} >>>> >>>> All the above does essentially the same things as the macro of >>>> axg-audio, to some minor differences. Yet it is another set to maintain. >>>> >>> >>> Except one thing... Here I keep memory identifier to which this clock >>> belongs: >>> >>> .map = AUDIO_REG_MAP(_reg), >>> >>> It is workaround, but ->map the only common field in clk_regmap that >>> could be used for this purpose. >>> >>> >>>> I'd much prefer if you put the axg-audio macro in a header a re-used >>>> those. There would a single set to maintain. You may then specialize the >>>> included in the driver C file, to avoid redundant parameters >>>> >>>> Rework axg-audio to use clk_parent_data if you must, but not in the same >>>> series please. >>>> >>>>> + >>>>> +static const struct clk_parent_data a1_pclk_pdata[] = { >>>>> + { .fw_name = "pclk", }, >>>>> +}; >>>>> + >>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata); >>>> This is what I mean by redundant parameter ^ >>>> >>> >>> Yep. I could define something like AUDIO_PCLK_GATE(). >>> >>>>> + >>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata); >>>>> + >>>>> +static const struct clk_parent_data a1_mst_pdata[] = { >>>>> + { .fw_name = "dds_in" }, >>>>> + { .fw_name = "fclk_div2" }, >>>>> + { .fw_name = "fclk_div3" }, >>>>> + { .fw_name = "hifi_pll" }, >>>>> + { .fw_name = "xtal" }, >>>>> +}; >>>>> + >>>>> +#define AUDIO_MST_MCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \ >>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \ >>>>> + AUDIO_PDATA(_name##_mux)); \ >>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div)) >>>>> + >>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL); >>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); >>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL); >>>>> + >>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8, >>>>> + AUDIO_PDATA(audio_resample_clk_mux)); >>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31, >>>>> + AUDIO_PDATA(audio_resample_clk_div)); >>>>> + >>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8, >>>>> + AUDIO_PDATA(audio_locker_in_clk_mux)); >>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15, >>>>> + AUDIO_PDATA(audio_locker_in_clk_div)); >>>>> + >>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8, >>>>> + AUDIO_PDATA(audio_locker_out_clk_mux)); >>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31, >>>>> + AUDIO_PDATA(audio_locker_out_clk_div)); >>>>> + >>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL); >>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL); >>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0); >>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1); >>>>> + >>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \ >>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \ >>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \ >>>>> + AUDIO_PDATA(_name##_pre_en), true); \ >>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \ >>>>> + AUDIO_PDATA(_name##_div)); \ >>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \ >>>>> + AUDIO_PDATA(_name##_post_en)) >>>>> + >>>> >>>> Again, I'm not a fan of this many levels of macro. I can live with it >>>> but certainly don't want the burden of reviewing and maintaining for >>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common. >>>> >>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \ >>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \ >>>>> + (_pdata), false); \ >>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \ >>>>> + AUDIO_PDATA(_name##_div)) >>>>> + >>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_a_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_b_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_c_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_d_mclk)); >>>>> + >>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en)); >>>>> + >>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = { >>>>> + { .hw = &audio_mst_a_sclk.hw }, >>>>> + { .hw = &audio_mst_b_sclk.hw }, >>>>> + { .hw = &audio_mst_c_sclk.hw }, >>>>> + { .hw = &audio_mst_d_sclk.hw }, >>>>> + { .fw_name = "slv_sclk0" }, >>>>> + { .fw_name = "slv_sclk1" }, >>>>> + { .fw_name = "slv_sclk2" }, >>>>> + { .fw_name = "slv_sclk3" }, >>>>> + { .fw_name = "slv_sclk4" }, >>>>> + { .fw_name = "slv_sclk5" }, >>>>> + { .fw_name = "slv_sclk6" }, >>>>> + { .fw_name = "slv_sclk7" }, >>>>> + { .fw_name = "slv_sclk8" }, >>>>> + { .fw_name = "slv_sclk9" }, >>>>> +}; >>>>> + >>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = { >>>>> + { .hw = &audio_mst_a_lrclk.hw }, >>>>> + { .hw = &audio_mst_b_lrclk.hw }, >>>>> + { .hw = &audio_mst_c_lrclk.hw }, >>>>> + { .hw = &audio_mst_d_lrclk.hw }, >>>>> + { .fw_name = "slv_lrclk0" }, >>>>> + { .fw_name = "slv_lrclk1" }, >>>>> + { .fw_name = "slv_lrclk2" }, >>>>> + { .fw_name = "slv_lrclk3" }, >>>>> + { .fw_name = "slv_lrclk4" }, >>>>> + { .fw_name = "slv_lrclk5" }, >>>>> + { .fw_name = "slv_lrclk6" }, >>>>> + { .fw_name = "slv_lrclk7" }, >>>>> + { .fw_name = "slv_lrclk8" }, >>>>> + { .fw_name = "slv_lrclk9" }, >>>>> +}; >>>>> + >>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \ >>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \ >>>>> + AUDIO_PDATA(_name##_mux)); \ >>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \ >>>>> + AUDIO_PDATA(_name##_pre_en)); \ >>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \ >>>>> + AUDIO_PDATA(_name##_post_en)) >>>>> + >>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata) >>>>> + >>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL); >>>>> + >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL); >>>>> + >>>>> +static struct clk_hw *a1_audio_hw_clks[] = { >>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw, >>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw, >>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw, >>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw, >>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw, >>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw, >>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw, >>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw, >>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw, >>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw, >>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw, >>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw, >>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw, >>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw, >>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw, >>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw, >>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw, >>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw, >>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw, >>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw, >>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw, >>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw, >>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw, >>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw, >>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw, >>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw, >>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw, >>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw, >>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw, >>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw, >>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw, >>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw, >>>>> + >>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw, >>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw, >>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw, >>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw, >>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw, >>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw, >>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw, >>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw, >>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw, >>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw, >>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw, >>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw, >>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw, >>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw, >>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw, >>>>> +}; >>>>> + >>>>> +static struct meson_clk_hw_data a1_audio_clks = { >>>>> + .hws = a1_audio_hw_clks, >>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks), >>>>> +}; >>>>> + >>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev, >>>>> + unsigned int index) >>>>> +{ >>>>> + char name[32]; >>>>> + const struct regmap_config cfg = { >>>>> + .reg_bits = 32, >>>>> + .val_bits = 32, >>>>> + .reg_stride = 4, >>>>> + .name = name, >>>> >>>> Not necessary >>>> >>> >>> This implementation uses two regmaps, and this field allow to avoid >>> errors like this: >>> >>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with >>> parent 'regmap' already present! >>> >>>>> + }; >>>>> + void __iomem *base; >>>>> + >>>>> + base = devm_platform_ioremap_resource(pdev, index); >>>>> + if (IS_ERR(base)) >>>>> + return base; >>>>> + >>>>> + scnprintf(name, sizeof(name), "%d", index); >>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg); >>>>> +} >>>> >>>> That is overengineered. Please keep it simple. Declare the regmap_config >>>> as static const global, and do it like axg-audio please. >>>> >>> >>> This only reason why it is not "static const" because I need to set >>> unique name for each regmap. >>> >>>>> + >>>>> +static int a1_register_clk(struct platform_device *pdev, >>>>> + struct regmap *map0, struct regmap *map1, >>>>> + struct clk_hw *hw) >>>>> +{ >>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw); >>>>> + >>>>> + if (!hw) >>>>> + return 0; >>>>> + >>>>> + switch ((unsigned long)clk->map) { >>>>> + case AUDIO_RANGE_0: >>>>> + clk->map = map0; >>>>> + break; >>>>> + case AUDIO_RANGE_1: >>>>> + clk->map = map1; >>>>> + break; >>>> >>>> ... fishy >>>> >>>>> + default: >>>>> + WARN_ON(1); >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + return devm_clk_hw_register(&pdev->dev, hw); >>>>> +} >>>>> + >>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev) >>>>> +{ >>>>> + struct regmap *map0, *map1; >>>>> + struct clk *clk; >>>>> + unsigned int i; >>>>> + int ret; >>>>> + >>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk"); >>>>> + if (WARN_ON(IS_ERR(clk))) >>>>> + return PTR_ERR(clk); >>>>> + >>>>> + map0 = a1_audio_map(pdev, 0); >>>>> + if (IS_ERR(map0)) >>>>> + return PTR_ERR(map0); >>>>> + >>>>> + map1 = a1_audio_map(pdev, 1); >>>>> + if (IS_ERR(map1)) >>>>> + return PTR_ERR(map1); >>>> >>>> No - Looks to me you just have two clock controllers you are trying >>>> force into one. >>>> >>> >>> See the begining. >>> >>>>> + >>>>> + /* >>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless >>>>> + * it is enabled any read/write to 'map0' hangs the CPU. >>>>> + */ >>>>> + >>>>> + ret = a1_register_clk(pdev, map0, map1, >>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk); >>>>> + if (ret) >>>>> + return ret; >>>> >>>> Again, this shows 2 devices. The one related to your 'map0' should >>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away. >>>> >>> >>> See the begining. >>> >>>>> + >>>>> + for (i = 0; i < a1_audio_clks.num; i++) { >>>>> + if (i == AUD2_CLKID_AUDIOTOP) >>>>> + continue; >>>>> + >>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]); >>>>> + if (ret) >>>>> + return ret; >>>>> + } >>>>> + >>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get, >>>>> + &a1_audio_clks); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) != >>>>> + AUDIO_RANGE_0); >>>> >>>> Why is that necessary ? >>>> >>> >>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register, >>> and I want to assert it. >>> >>>>> + return meson_audio_rstc_register(&pdev->dev, map0, >>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32); >>>>> +} >>>>> + >>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = { >>>>> + { .compatible = "amlogic,a1-audio-clkc", }, >>>>> + {} >>>>> +}; >>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table); >>>>> + >>>>> +static struct platform_driver a1_audio_clkc_driver = { >>>>> + .probe = a1_audio_clkc_probe, >>>>> + .driver = { >>>>> + .name = "a1-audio-clkc", >>>>> + .of_match_table = a1_audio_clkc_match_table, >>>>> + }, >>>>> +}; >>>>> +module_platform_driver(a1_audio_clkc_driver); >>>>> + >>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver"); >>>>> +MODULE_AUTHOR("Jan Dakinevich "); >>>>> +MODULE_LICENSE("GPL"); >>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h >>>>> new file mode 100644 >>>>> index 000000000000..f994e87276cd >>>>> --- /dev/null >>>>> +++ b/drivers/clk/meson/a1-audio.h >>>>> @@ -0,0 +1,58 @@ >>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ >>>>> +/* >>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. >>>>> + * >>>>> + * Author: Jan Dakinevich >>>>> + */ >>>>> + >>>>> +#ifndef __A1_AUDIO_H >>>>> +#define __A1_AUDIO_H >>>>> + >>>>> +#define AUDIO_RANGE_0 0xa >>>>> +#define AUDIO_RANGE_1 0xb >>>>> +#define AUDIO_RANGE_SHIFT 16 >>>>> + >>>>> +#define AUDIO_REG(_range, _offset) \ >>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset)) >>>>> + >>>>> +#define AUDIO_REG_OFFSET(_reg) \ >>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1)) >>>>> + >>>>> +#define AUDIO_REG_MAP(_reg) \ >>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT)) >>>> >>>> That is seriouly overengineered. >>>> The following are offset. Just write what they are. >>>> >>> >>> This is all in order to keep range's identifier together with offset and >>> then use it to store the identifier in clk_regmaps. >>> >>>> There is not reason to put that into a header. It is only going to be >>>> used by a single driver. >>>>>> + >>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000) >>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008) >>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c) >>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010) >>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014) >>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018) >>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c) >>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028) >>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040) >>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044) >>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048) >>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c) >>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050) >>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054) >>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058) >>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c) >>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080) >>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084) >>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c) >>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090) >>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094) >>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c) >>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4) >>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8) >>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0) >>>>> + >>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c) >>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040) >>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044) >>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058) >>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c) >>>>> + >>>>> +#include >>>>> + >>>>> +#endif /* __A1_AUDIO_H */ >>>> >>>> >> >> -- Jerome _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3BD4BC6FD1F for ; Tue, 26 Mar 2024 18:03:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:In-reply-to: Date:Subject:Cc:To:From:References:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=ttucp8rmILH5pcTo9X3VQ3otBRKknv+HxaGSpF4JDkc=; b=f1eO9Rd4v6OwKn iOpBo6y3AQfKQ6/TwB9rkXeW0Rk4uhHT3ef5jgG15yPqfKjZIoO37N+7py5NKRQDsGhd6r/+TaCeB biRQsRjjFYohKkkldFYJxVJlsm4gSlZW8UVHx+UAuGiUWBsPnd4es/PEY/uK57djNkKotqCqloglK cZS8E6lB71YW+0RTz4SoWuga7gfgFb+IZcIgm48d4u4M3/0NL2xBt6zvQRPeaYAp1EgA1ZD6hQmBO 2i8nD+poYorqlo9DUvrsRGuBH1/T7rKblDXHSrfOdoMfezJ8e28COwtHs2WuGwc30Y+fr7c5nv5QA uPGdcRdVFXnY01Omil4Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rpB8e-00000005qb0-2wSz; Tue, 26 Mar 2024 18:03:08 +0000 Received: from mail-wm1-f49.google.com ([209.85.128.49]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rp8vz-00000005GCv-3kg2 for linux-arm-kernel@lists.infradead.org; Tue, 26 Mar 2024 15:42:12 +0000 Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-4141156f245so38443255e9.2 for ; Tue, 26 Mar 2024 08:41:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1711467713; x=1712072513; darn=lists.infradead.org; h=mime-version:message-id:in-reply-to:date:subject:cc:to:from :user-agent:references:from:to:cc:subject:date:message-id:reply-to; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=tweF+27EfuV67DXBxos/sek1rWsxf1+leaXmjBQpAXo6MLe8KXHpKkaOGBOtzQpwF2 cA8MdREC2W/j9rN8qgyu/HRdKY8pwLewK+hM+JJoNK26aYjXMuvrxmAOjvfC0BQ6KGCe JBa0CNlxSVdkfFR0QnfRuCA1XwP7MLXD5ezaUFw+4NG+v4CybXsjT5Qc4FS2uT9jn073 5Ucyo3PAW7NwhyGoXekvWRjbu3PcVtui7VZ0fmKGUtOvWHIHnCws0hy+a3OFghGIlRX5 /MyXwYR//otK8kLTFvM6GAV4zjiVz5+ODemtQhVbnAdgETdy6lDfG1aLsOk9Ekm1KFLA dl3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711467713; x=1712072513; h=mime-version:message-id:in-reply-to:date:subject:cc:to:from :user-agent:references:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=mP/xNhGIhG5EjKaAvG6LSDISrRK3up93nGx/4qT1+IQ=; b=jog0mGWTPoifdWoCiG3rx58m48HEV3ksQLK43pn9Lmi6tb/7OfqFToPMYGa7VKSmle 5ojIPoNWIURQSpYgr6RZdc/jLnNgeXp429mJRZvN4IEHr19Fm+8G0/Tu8aWYs+FgKdgz 3yiU3BC1vaHi0WKFdz33w69Gje2mQqlSNiVc14g1tDzmTuzxMT38T0unppmwA7HJ1jnO b5tKyCz22WQOXrM049CVFi454h25Sztfv3yD4PI5bA0GDNRIXScaqWp7PbgAN5l0IWoQ dRgEW9iIjcZtyrxQhpWq46RA9z2OVgnhrl28OtZedYWBekU7NubKmAxtm8HL6968sYYj O1Lw== X-Forwarded-Encrypted: i=1; AJvYcCXmAuj8vkx4aqgTRfmGop/W8eP2dMzYXXVVy+jsA9U7YCE59CyNJd4xEcDbvKY6c4X/5QYFbROg2xUbqRV4pyA5e1McpeXdWZxoNLkeHtDFUyeyFYA= X-Gm-Message-State: AOJu0YxJw/hUkPw8vi4bYhlzSa00jF8D+BdizjWY0SUV73Cy5bFjK8/k ktR3/tdzY4p9EIkO4WxMsjRAbYTC5PpIjzP7i2FOskglBcps/xPvQ/flQNOrd9U= X-Google-Smtp-Source: AGHT+IH7q/cpz8ILaErtLAXwphlwM1LTCLYtF6V5eMpQldTUIrcdWn3kgNQyW/tgQYAiJ+q7VBw8dw== X-Received: by 2002:a7b:cd08:0:b0:413:ea5c:1b08 with SMTP id f8-20020a7bcd08000000b00413ea5c1b08mr6400986wmj.40.1711467712539; Tue, 26 Mar 2024 08:41:52 -0700 (PDT) Received: from localhost ([2a01:e0a:3c5:5fb1:97a2:bb9f:463a:6468]) by smtp.gmail.com with ESMTPSA id l21-20020a05600c4f1500b004148ab95c36sm5397116wmq.41.2024.03.26.08.41.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Mar 2024 08:41:52 -0700 (PDT) References: <20240314232201.2102178-1-jan.dakinevich@salutedevices.com> <20240314232201.2102178-5-jan.dakinevich@salutedevices.com> <1j4jd7izx4.fsf@starbuckisacylon.baylibre.com> <6c129ee2-f08c-4575-a95f-667cc6472578@salutedevices.com> <1jo7bafv90.fsf@starbuckisacylon.baylibre.com> <0f4b2ec3-204f-44ea-a1b2-e47ea05def1f@salutedevices.com> User-agent: mu4e 1.10.8; emacs 29.2 From: Jerome Brunet To: Jan Dakinevich Cc: Jerome Brunet , Neil Armstrong , Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Kevin Hilman , Martin Blumenstingl , Liam Girdwood , Mark Brown , Linus Walleij , Jaroslav Kysela , Takashi Iwai , linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org, linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org, kernel@salutedevices.com Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver Date: Tue, 26 Mar 2024 16:26:52 +0100 In-reply-to: <0f4b2ec3-204f-44ea-a1b2-e47ea05def1f@salutedevices.com> Message-ID: <1jy1a580g0.fsf@starbuckisacylon.baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240326_084156_768285_099C3EC2 X-CRM114-Status: GOOD ( 32.55 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org On Sat 23 Mar 2024 at 21:02, Jan Dakinevich wrote: > Jerome, I have reworked my driver reusing axg-audio code as most as I > could and now I have one more question. Lets see on this definition from > axg-audio: > > #define AUD_MST_MUX(_name, _reg, _flag) \ > AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \ > mst_mux_parent_data, 0) > > #define AUD_MST_MCLK_MUX(_name, _reg) \ > AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST) > > CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll > clock will not be reconfigured at runtime to satisfy the rate that was > requested from axg-tdm. > Yes, that is by design. It is another area where mainline audio differs greatly from AML vendor code. The PLLs are expected be to fixed and the audio master clock will reparent to the most adequate PLL source depending on the use case. This is how we manage to satisfy all audio interfaces with a very limited number of PLLs On AXG/G12 there is at most 6 concurrent interfaces (3 FRDDR/TODDR) - 8 on sm1 - and we can satisfy on that with 3 PLLs. That would not be possible if interfaces were having their way with the PLLs, reseting it everytime a stream is started. The PLL rate should be carefully chosen so it can be derived easily. On AXG/G12/SM1 that is: * one PLL per rate family, to maximize clock precision * x24 x32: to handle different sample sizes * x2 until we reach the PLL limits to allow higher rates such as 384kHz or even higher If you have less PLLs on A1, you'll have to make compromises, like a less precise clock to support multiple family with one PLL. This is why the PLLs are set for each platform in DT because that choice may depend on the platform use case. > > On 3/19/24 11:30, Jerome Brunet wrote: >> >> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich wrote: >> >>> Let's start from the end: >>> >>>> No - Looks to me you just have two clock controllers you are trying >>> force into one. >>> >>>> Again, this shows 2 devices. The one related to your 'map0' should >>> request AUD2_CLKID_AUDIOTOP as input and enable it right away. >>> >>> Most of fishy workarounds that you commented is caused the fact the mmio >>> of this clock controller is divided into two parts. Compare it with >>> axg-audio driver, things that was part of contigous memory region (like >>> pdm) here are moved to second region. Is this enough to make a guess >>> that these are two devices? >> >> I see obsolutely no reason to think it is a single device nor to add all the quirks >> you have the way you did. So yes, in that case, 2 zones, 2 devices. >> >>> >>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be >>> enabled before enabling of clocks from second region too. That is >>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller. >>> >> >> Yes. I understood the first time around and already commented on that. >> >>> >>> On 3/15/24 12:20, Jerome Brunet wrote: >>>> >>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich wrote: >>>> >>>>> This controller provides clocks and reset functionality for audio >>>>> peripherals on Amlogic A1 SoC family. >>>>> >>>>> The driver is almost identical to 'axg-audio', however it would be better >>>>> to keep it separate due to following reasons: >>>>> >>>>> - significant amount of bits has another definition. I will bring there >>>>> a mess of new defines with A1_ suffixes. >>>>> >>>>> - registers of this controller are located in two separate regions. It >>>>> will give a lot of complications for 'axg-audio' to support this. >>>>> >>>>> Signed-off-by: Jan Dakinevich >>>>> --- >>>>> drivers/clk/meson/Kconfig | 13 + >>>>> drivers/clk/meson/Makefile | 1 + >>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++ >>>>> drivers/clk/meson/a1-audio.h | 58 ++++ >>>>> 4 files changed, 628 insertions(+) >>>>> create mode 100644 drivers/clk/meson/a1-audio.c >>>>> create mode 100644 drivers/clk/meson/a1-audio.h >>>>> >>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig >>>>> index d6a2fa5f7e88..80c4a18c83d2 100644 >>>>> --- a/drivers/clk/meson/Kconfig >>>>> +++ b/drivers/clk/meson/Kconfig >>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS >>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock >>>>> controller to work. >>>>> >>>>> +config COMMON_CLK_A1_AUDIO >>>>> + tristate "Amlogic A1 SoC Audio clock controller support" >>>>> + depends on ARM64 >>>>> + select COMMON_CLK_MESON_REGMAP >>>>> + select COMMON_CLK_MESON_CLKC_UTILS >>>>> + select COMMON_CLK_MESON_PHASE >>>>> + select COMMON_CLK_MESON_SCLK_DIV >>>>> + select COMMON_CLK_MESON_AUDIO_RSTC >>>>> + help >>>>> + Support for the Audio clock controller on Amlogic A113L based >>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller >>>>> + to work. >>>>> + >>>>> config COMMON_CLK_G12A >>>>> tristate "G12 and SM1 SoC clock controllers support" >>>>> depends on ARM64 >>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile >>>>> index 88d94921a4dc..4968fc7ad555 100644 >>>>> --- a/drivers/clk/meson/Makefile >>>>> +++ b/drivers/clk/meson/Makefile >>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o >>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o >>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o >>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o >>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o >>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o >>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c >>>>> new file mode 100644 >>>>> index 000000000000..6039116c93ba >>>>> --- /dev/null >>>>> +++ b/drivers/clk/meson/a1-audio.c >>>>> @@ -0,0 +1,556 @@ >>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT) >>>>> +/* >>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. >>>>> + * >>>>> + * Author: Jan Dakinevich >>>>> + */ >>>>> + >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> +#include >>>>> + >>>>> +#include "meson-clkc-utils.h" >>>>> +#include "meson-audio-rstc.h" >>>>> +#include "clk-regmap.h" >>>>> +#include "clk-phase.h" >>>>> +#include "sclk-div.h" >>>>> +#include "a1-audio.h" >>>>> + >>>>> +#define AUDIO_PDATA(_name) \ >>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } }) >>>> >>>> Not a fan - yet another level of macro. >>>> >>>>> + >>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_mux_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .mask = (_mask), \ >>>>> + .shift = (_shift), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_mux_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = ARRAY_SIZE(_pdata), \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_div_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_divider_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct clk_regmap_gate_data){ \ >>>>> + .offset = AUDIO_REG_OFFSET(_reg), \ >>>>> + .bit_idx = (_bit), \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &clk_regmap_gate_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \ >>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_sclk_div_data) { \ >>>>> + .div = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_div_shift), \ >>>>> + .width = (_div_width), \ >>>>> + }, \ >>>>> + .hi = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_hi_shift), \ >>>>> + .width = (_hi_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_sclk_div_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \ >>>> >>>> Does not help readeability. Just pass the flag as axg-audio does. >>>> >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \ >>>>> + _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_clk_triphase_data) { \ >>>>> + .ph0 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift0), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ph1 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift1), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ph2 = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift2), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_clk_triphase_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \ >>>>> + _pdata) \ >>>>> +static struct clk_regmap _name = { \ >>>>> + .map = AUDIO_REG_MAP(_reg), \ >>>>> + .data = &(struct meson_sclk_ws_inv_data) { \ >>>>> + .ph = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift_ph), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + .ws = { \ >>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \ >>>>> + .shift = (_shift_ws), \ >>>>> + .width = (_width), \ >>>>> + }, \ >>>>> + }, \ >>>>> + .hw.init = &(struct clk_init_data) { \ >>>>> + .name = #_name, \ >>>>> + .ops = &meson_sclk_ws_inv_ops, \ >>>>> + .parent_data = (_pdata), \ >>>>> + .num_parents = 1, \ >>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \ >>>>> + }, \ >>>>> +} >>>> >>>> All the above does essentially the same things as the macro of >>>> axg-audio, to some minor differences. Yet it is another set to maintain. >>>> >>> >>> Except one thing... Here I keep memory identifier to which this clock >>> belongs: >>> >>> .map = AUDIO_REG_MAP(_reg), >>> >>> It is workaround, but ->map the only common field in clk_regmap that >>> could be used for this purpose. >>> >>> >>>> I'd much prefer if you put the axg-audio macro in a header a re-used >>>> those. There would a single set to maintain. You may then specialize the >>>> included in the driver C file, to avoid redundant parameters >>>> >>>> Rework axg-audio to use clk_parent_data if you must, but not in the same >>>> series please. >>>> >>>>> + >>>>> +static const struct clk_parent_data a1_pclk_pdata[] = { >>>>> + { .fw_name = "pclk", }, >>>>> +}; >>>>> + >>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata); >>>> This is what I mean by redundant parameter ^ >>>> >>> >>> Yep. I could define something like AUDIO_PCLK_GATE(). >>> >>>>> + >>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata); >>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata); >>>>> + >>>>> +static const struct clk_parent_data a1_mst_pdata[] = { >>>>> + { .fw_name = "dds_in" }, >>>>> + { .fw_name = "fclk_div2" }, >>>>> + { .fw_name = "fclk_div3" }, >>>>> + { .fw_name = "hifi_pll" }, >>>>> + { .fw_name = "xtal" }, >>>>> +}; >>>>> + >>>>> +#define AUDIO_MST_MCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \ >>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \ >>>>> + AUDIO_PDATA(_name##_mux)); \ >>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div)) >>>>> + >>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL); >>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL); >>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); >>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL); >>>>> + >>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8, >>>>> + AUDIO_PDATA(audio_resample_clk_mux)); >>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31, >>>>> + AUDIO_PDATA(audio_resample_clk_div)); >>>>> + >>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8, >>>>> + AUDIO_PDATA(audio_locker_in_clk_mux)); >>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15, >>>>> + AUDIO_PDATA(audio_locker_in_clk_div)); >>>>> + >>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24, >>>>> + a1_mst_pdata); >>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8, >>>>> + AUDIO_PDATA(audio_locker_out_clk_mux)); >>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31, >>>>> + AUDIO_PDATA(audio_locker_out_clk_div)); >>>>> + >>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL); >>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL); >>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0); >>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1); >>>>> + >>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \ >>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \ >>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \ >>>>> + AUDIO_PDATA(_name##_pre_en), true); \ >>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \ >>>>> + AUDIO_PDATA(_name##_div)); \ >>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \ >>>>> + AUDIO_PDATA(_name##_post_en)) >>>>> + >>>> >>>> Again, I'm not a fan of this many levels of macro. I can live with it >>>> but certainly don't want the burden of reviewing and maintaining for >>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common. >>>> >>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \ >>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \ >>>>> + (_pdata), false); \ >>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \ >>>>> + AUDIO_PDATA(_name##_div)) >>>>> + >>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_a_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_b_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_c_mclk)); >>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_d_mclk)); >>>>> + >>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en)); >>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1, >>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en)); >>>>> + >>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = { >>>>> + { .hw = &audio_mst_a_sclk.hw }, >>>>> + { .hw = &audio_mst_b_sclk.hw }, >>>>> + { .hw = &audio_mst_c_sclk.hw }, >>>>> + { .hw = &audio_mst_d_sclk.hw }, >>>>> + { .fw_name = "slv_sclk0" }, >>>>> + { .fw_name = "slv_sclk1" }, >>>>> + { .fw_name = "slv_sclk2" }, >>>>> + { .fw_name = "slv_sclk3" }, >>>>> + { .fw_name = "slv_sclk4" }, >>>>> + { .fw_name = "slv_sclk5" }, >>>>> + { .fw_name = "slv_sclk6" }, >>>>> + { .fw_name = "slv_sclk7" }, >>>>> + { .fw_name = "slv_sclk8" }, >>>>> + { .fw_name = "slv_sclk9" }, >>>>> +}; >>>>> + >>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = { >>>>> + { .hw = &audio_mst_a_lrclk.hw }, >>>>> + { .hw = &audio_mst_b_lrclk.hw }, >>>>> + { .hw = &audio_mst_c_lrclk.hw }, >>>>> + { .hw = &audio_mst_d_lrclk.hw }, >>>>> + { .fw_name = "slv_lrclk0" }, >>>>> + { .fw_name = "slv_lrclk1" }, >>>>> + { .fw_name = "slv_lrclk2" }, >>>>> + { .fw_name = "slv_lrclk3" }, >>>>> + { .fw_name = "slv_lrclk4" }, >>>>> + { .fw_name = "slv_lrclk5" }, >>>>> + { .fw_name = "slv_lrclk6" }, >>>>> + { .fw_name = "slv_lrclk7" }, >>>>> + { .fw_name = "slv_lrclk8" }, >>>>> + { .fw_name = "slv_lrclk9" }, >>>>> +}; >>>>> + >>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \ >>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \ >>>>> + AUDIO_PDATA(_name##_mux)); \ >>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \ >>>>> + AUDIO_PDATA(_name##_pre_en)); \ >>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \ >>>>> + AUDIO_PDATA(_name##_post_en)) >>>>> + >>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \ >>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata) >>>>> + >>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL); >>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL); >>>>> + >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL); >>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL); >>>>> + >>>>> +static struct clk_hw *a1_audio_hw_clks[] = { >>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw, >>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw, >>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw, >>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw, >>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw, >>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw, >>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw, >>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw, >>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw, >>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw, >>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw, >>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw, >>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw, >>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw, >>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw, >>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw, >>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw, >>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw, >>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw, >>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw, >>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw, >>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw, >>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw, >>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw, >>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw, >>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw, >>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw, >>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw, >>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw, >>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw, >>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw, >>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw, >>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw, >>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw, >>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw, >>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw, >>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw, >>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw, >>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw, >>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw, >>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw, >>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw, >>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw, >>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw, >>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw, >>>>> + >>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw, >>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw, >>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw, >>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw, >>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw, >>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw, >>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw, >>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw, >>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw, >>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw, >>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw, >>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw, >>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw, >>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw, >>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw, >>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw, >>>>> +}; >>>>> + >>>>> +static struct meson_clk_hw_data a1_audio_clks = { >>>>> + .hws = a1_audio_hw_clks, >>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks), >>>>> +}; >>>>> + >>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev, >>>>> + unsigned int index) >>>>> +{ >>>>> + char name[32]; >>>>> + const struct regmap_config cfg = { >>>>> + .reg_bits = 32, >>>>> + .val_bits = 32, >>>>> + .reg_stride = 4, >>>>> + .name = name, >>>> >>>> Not necessary >>>> >>> >>> This implementation uses two regmaps, and this field allow to avoid >>> errors like this: >>> >>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with >>> parent 'regmap' already present! >>> >>>>> + }; >>>>> + void __iomem *base; >>>>> + >>>>> + base = devm_platform_ioremap_resource(pdev, index); >>>>> + if (IS_ERR(base)) >>>>> + return base; >>>>> + >>>>> + scnprintf(name, sizeof(name), "%d", index); >>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg); >>>>> +} >>>> >>>> That is overengineered. Please keep it simple. Declare the regmap_config >>>> as static const global, and do it like axg-audio please. >>>> >>> >>> This only reason why it is not "static const" because I need to set >>> unique name for each regmap. >>> >>>>> + >>>>> +static int a1_register_clk(struct platform_device *pdev, >>>>> + struct regmap *map0, struct regmap *map1, >>>>> + struct clk_hw *hw) >>>>> +{ >>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw); >>>>> + >>>>> + if (!hw) >>>>> + return 0; >>>>> + >>>>> + switch ((unsigned long)clk->map) { >>>>> + case AUDIO_RANGE_0: >>>>> + clk->map = map0; >>>>> + break; >>>>> + case AUDIO_RANGE_1: >>>>> + clk->map = map1; >>>>> + break; >>>> >>>> ... fishy >>>> >>>>> + default: >>>>> + WARN_ON(1); >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + return devm_clk_hw_register(&pdev->dev, hw); >>>>> +} >>>>> + >>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev) >>>>> +{ >>>>> + struct regmap *map0, *map1; >>>>> + struct clk *clk; >>>>> + unsigned int i; >>>>> + int ret; >>>>> + >>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk"); >>>>> + if (WARN_ON(IS_ERR(clk))) >>>>> + return PTR_ERR(clk); >>>>> + >>>>> + map0 = a1_audio_map(pdev, 0); >>>>> + if (IS_ERR(map0)) >>>>> + return PTR_ERR(map0); >>>>> + >>>>> + map1 = a1_audio_map(pdev, 1); >>>>> + if (IS_ERR(map1)) >>>>> + return PTR_ERR(map1); >>>> >>>> No - Looks to me you just have two clock controllers you are trying >>>> force into one. >>>> >>> >>> See the begining. >>> >>>>> + >>>>> + /* >>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless >>>>> + * it is enabled any read/write to 'map0' hangs the CPU. >>>>> + */ >>>>> + >>>>> + ret = a1_register_clk(pdev, map0, map1, >>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk); >>>>> + if (ret) >>>>> + return ret; >>>> >>>> Again, this shows 2 devices. The one related to your 'map0' should >>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away. >>>> >>> >>> See the begining. >>> >>>>> + >>>>> + for (i = 0; i < a1_audio_clks.num; i++) { >>>>> + if (i == AUD2_CLKID_AUDIOTOP) >>>>> + continue; >>>>> + >>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]); >>>>> + if (ret) >>>>> + return ret; >>>>> + } >>>>> + >>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get, >>>>> + &a1_audio_clks); >>>>> + if (ret) >>>>> + return ret; >>>>> + >>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) != >>>>> + AUDIO_RANGE_0); >>>> >>>> Why is that necessary ? >>>> >>> >>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register, >>> and I want to assert it. >>> >>>>> + return meson_audio_rstc_register(&pdev->dev, map0, >>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32); >>>>> +} >>>>> + >>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = { >>>>> + { .compatible = "amlogic,a1-audio-clkc", }, >>>>> + {} >>>>> +}; >>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table); >>>>> + >>>>> +static struct platform_driver a1_audio_clkc_driver = { >>>>> + .probe = a1_audio_clkc_probe, >>>>> + .driver = { >>>>> + .name = "a1-audio-clkc", >>>>> + .of_match_table = a1_audio_clkc_match_table, >>>>> + }, >>>>> +}; >>>>> +module_platform_driver(a1_audio_clkc_driver); >>>>> + >>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver"); >>>>> +MODULE_AUTHOR("Jan Dakinevich "); >>>>> +MODULE_LICENSE("GPL"); >>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h >>>>> new file mode 100644 >>>>> index 000000000000..f994e87276cd >>>>> --- /dev/null >>>>> +++ b/drivers/clk/meson/a1-audio.h >>>>> @@ -0,0 +1,58 @@ >>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ >>>>> +/* >>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. >>>>> + * >>>>> + * Author: Jan Dakinevich >>>>> + */ >>>>> + >>>>> +#ifndef __A1_AUDIO_H >>>>> +#define __A1_AUDIO_H >>>>> + >>>>> +#define AUDIO_RANGE_0 0xa >>>>> +#define AUDIO_RANGE_1 0xb >>>>> +#define AUDIO_RANGE_SHIFT 16 >>>>> + >>>>> +#define AUDIO_REG(_range, _offset) \ >>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset)) >>>>> + >>>>> +#define AUDIO_REG_OFFSET(_reg) \ >>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1)) >>>>> + >>>>> +#define AUDIO_REG_MAP(_reg) \ >>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT)) >>>> >>>> That is seriouly overengineered. >>>> The following are offset. Just write what they are. >>>> >>> >>> This is all in order to keep range's identifier together with offset and >>> then use it to store the identifier in clk_regmaps. >>> >>>> There is not reason to put that into a header. It is only going to be >>>> used by a single driver. >>>>>> + >>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000) >>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008) >>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c) >>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010) >>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014) >>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018) >>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c) >>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028) >>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040) >>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044) >>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048) >>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c) >>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050) >>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054) >>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058) >>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c) >>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080) >>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084) >>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c) >>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090) >>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094) >>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c) >>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4) >>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8) >>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0) >>>>> + >>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c) >>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040) >>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044) >>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058) >>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c) >>>>> + >>>>> +#include >>>>> + >>>>> +#endif /* __A1_AUDIO_H */ >>>> >>>> >> >> -- Jerome _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel