Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v7 08/14] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
From: Adrian Hunter @ 2016-11-16  7:42 UTC (permalink / raw)
  To: Ritesh Harjani, Stephen Boyd, ulf.hansson
  Cc: linux-mmc, shawn.lin, andy.gross, devicetree, linux-clk,
	david.brown, linux-arm-msm, georgi.djakov, alex.lemberg,
	mateusz.nowak, Yuliy.Izrailov, asutoshd, kdorfman, david.griego,
	stummala, venkatg, rnayak, pramod.gurav
In-Reply-To: <d723b9fb-0cfc-e72b-ad78-36c8b996b012@codeaurora.org>

On 16/11/16 06:42, Ritesh Harjani wrote:
> Hi,
> 
> On 11/15/2016 10:40 AM, Ritesh Harjani wrote:
>> Hi Stephen/Adrian,
>>
>> On 11/15/2016 1:07 AM, Stephen Boyd wrote:
>>> On 11/14, Ritesh Harjani wrote:
>>>> @@ -577,6 +578,90 @@ static unsigned int
>>>> sdhci_msm_get_min_clock(struct sdhci_host *host)
>>>>      return SDHCI_MSM_MIN_CLOCK;
>>>>  }
>>>>
>>>> +/**
>>>> + * __sdhci_msm_set_clock - sdhci_msm clock control.
>>>> + *
>>>> + * Description:
>>>> + * Implement MSM version of sdhci_set_clock.
>>>> + * This is required since MSM controller does not
>>>> + * use internal divider and instead directly control
>>>> + * the GCC clock as per HW recommendation.
>>>> + **/
>>>> +void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
>>>> +{
>>>> +    u16 clk;
>>>> +    unsigned long timeout;
>>>> +
>>>> +    /*
>>>> +     * Keep actual_clock as zero -
>>>> +     * - since there is no divider used so no need of having
>>>> actual_clock.
>>>> +     * - MSM controller uses SDCLK for data timeout calculation. If
>>>> +     *   actual_clock is zero, host->clock is taken for calculation.
>>>> +     */
>>>> +    host->mmc->actual_clock = 0;
>>>> +
>>>> +    sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
>>>> +
>>>> +    if (clock == 0)
>>>> +        return;
>>>> +
>>>> +    /*
>>>> +     * MSM controller do not use clock divider.
>>>> +     * Thus read SDHCI_CLOCK_CONTROL and only enable
>>>> +     * clock with no divider value programmed.
>>>> +     */
>>>> +    clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
>>>> +
>>>> +    clk |= SDHCI_CLOCK_INT_EN;
>>>> +    sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>>>> +
>>>> +    /* Wait max 20 ms */
>>>> +    timeout = 20;
>>>> +    while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
>>>> +        & SDHCI_CLOCK_INT_STABLE)) {
>>>> +        if (timeout == 0) {
>>>> +            pr_err("%s: Internal clock never stabilised\n",
>>>> +                   mmc_hostname(host->mmc));
>>>> +            return;
>>>> +        }
>>>> +        timeout--;
>>>> +        mdelay(1);
>>>> +    }
>>>> +
>>>> +    clk |= SDHCI_CLOCK_CARD_EN;
>>>> +    sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>>>
>>> This is almost a copy/paste of sdhci_set_clock(). Can we make
>>> sdhci_set_clock() call a __sdhci_set_clock() function that takes
>>> unsigned int clock, and also a flag indicating if we want to set
>>> the internal clock divider or not? Then we can call
>>> __sdhci_set_clock() from sdhci_set_clock() with (clock, true) as
>>> arguments and (clock, false).
> Actually what you may be referring here is some sort of quirks which is not
> entertained any more for sdhci driver.
> sdhci is tending towards becoming a library and hence such changes are
> restricted.
> 
> But I think we may do below changes to avoid duplication of code which
> enables the sdhci internal clock and waits for internal clock to be stable.
> 
> Adrian, could you please tell if this should be ok?

That seems fine, but the name seems too long - how about changing
sdhci_set_clock_enable to sdhci_enable_clk.

> Then we may be able to call for sdhci_set_clock_enable function from
> sdhci_msm_set_clock.
> 
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 42ef3eb..28e605c 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1343,19 +1343,8 @@ u16 sdhci_calc_clk(struct sdhci_host *host, unsigned
> int clock,
>  }
>  EXPORT_SYMBOL_GPL(sdhci_calc_clk);
> 
> -void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> +void sdhci_set_clock_enable(struct sdhci_host *host, unsigned short clk)
>  {
> -       u16 clk;
> -       unsigned long timeout;
> -
> -       host->mmc->actual_clock = 0;
> -
> -       sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
> -
> -       if (clock == 0)
> -               return;
> -
> -       clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
> 
>         clk |= SDHCI_CLOCK_INT_EN;
>         sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> @@ -1377,6 +1366,24 @@ void sdhci_set_clock(struct sdhci_host *host,
> unsigned int clock)
>         clk |= SDHCI_CLOCK_CARD_EN;
>         sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>  }
> +EXPORT_SYMBOL_GPL(sdhci_set_clock_enable);
> +
> +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> +{
> +       u16 clk;
> +       unsigned long timeout;
> +
> +       host->mmc->actual_clock = 0;
> +
> +       sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
> +
> +       if (clock == 0)
> +               return;
> +
> +       clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
> +
> +       sdhci_set_clock_enable(host, clk);
> +}
>  EXPORT_SYMBOL_GPL(sdhci_set_clock);
> 
>  static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode,
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 766df17..43382e1 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -681,6 +681,7 @@ static inline bool sdhci_sdio_irq_enabled(struct
> sdhci_host *host)
>  u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
>                    unsigned int *actual_clock);
>  void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
> +void sdhci_set_clock_enable(struct sdhci_host *host, unsigned short clk);
>  void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
>                      unsigned short vdd);
>  void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
> 
> 
> 
>> Adrian,
>> Could you please comment here ?
>>
>>>
>>>> +}
>>>> +
>>>> +/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
>>>> +static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned
>>>> int clock)
>>>> +{
>>>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>>> +    struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
>>>> +    int rc;
>>>> +
>>>> +    if (!clock) {
>>>> +        msm_host->clk_rate = clock;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    spin_unlock_irq(&host->lock);
>>>> +    if (clock != msm_host->clk_rate) {
>>>
>>> Why do we need to check here? Can't we call clk_set_rate()
>>> Unconditionally?
>> Since it may so happen that above layers may call for ->set_clock
>> function with same requested clock more than once, hence we cache the
>> host->clock here.
>> Also, since requested clock (host->clock) can be say 400Mhz but the
>> actual pltfm supported clock would be say 384MHz.
>>
>>
>>>
>>>> +        rc = clk_set_rate(msm_host->clk, clock);
>>>> +        if (rc) {
>>>> +            pr_err("%s: Failed to set clock at rate %u\n",
>>>> +                   mmc_hostname(host->mmc), clock);
>>>> +            spin_lock_irq(&host->lock);
>>>> +            goto out;
>>>
>>> Or replace the above two lines with goto err;
>> Ok, I will have another label out_lock instead of err.
>>>
>>>> +        }
>>>> +        msm_host->clk_rate = clock;
>>>> +        pr_debug("%s: Setting clock at rate %lu\n",
>>>> +             mmc_hostname(host->mmc), clk_get_rate(msm_host->clk));
>>>> +    }
>>>
>>> And put an err label here.
>> will put the label here as out_lock;
>>>
>>>> +    spin_lock_irq(&host->lock);
>>>> +out:
>>>> +    __sdhci_msm_set_clock(host, clock);
>>>> +}
>>>> +
>>>>  static const struct of_device_id sdhci_msm_dt_match[] = {
>>>>      { .compatible = "qcom,sdhci-msm-v4" },
>>>>      {},
>>>
>>
> 

^ permalink raw reply

* Re: [upstream-release] [PATCH 1/2] drivers: usb: phy: Add qoriq usb 3.0 phy driver support
From: Scott Wood @ 2016-11-16  7:22 UTC (permalink / raw)
  To: Sriram Dash, linux-kernel@vger.kernel.org,
	linux-usb@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
  Cc: mark.rutland@arm.com, felipe.balbi@linux.intel.com,
	mathias.nyman@intel.com, catalin.marinas@arm.com,
	will.deacon@arm.com, kishon@ti.com, robh+dt@kernel.org,
	stern@rowland.harvard.edu, Suresh Gupta,
	gregkh@linuxfoundation.org, pku.leo@gmail.com
In-Reply-To: <DB5PR0401MB1925FB1FE45C15B0671E46D1F5BF0@DB5PR0401MB1925.eurprd04.prod.outlook.com>

On 11/15/2016 06:39 AM, Sriram Dash wrote:
>> From: Scott Wood
>> On 11/13/2016 11:27 PM, Sriram Dash wrote:
>>> diff --git a/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>>> b/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>>> new file mode 100644
>>> index 0000000..d934c80
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>>> @@ -0,0 +1,36 @@
>>> +Driver for Freescale USB 3.0 PHY
>>> +
>>> +Required properties:
>>> +
>>> +- compatible :	fsl,qoriq-usb3-phy
>>
> 
> Hi Scott,
> 
>> This is a very vague compatible.  Are there versioning registers within this register
>> block?
>>
> 
> There are versioning registers for the phy (1.0 and 1.1). But the current erratum
> A008751 does not require the mentioning of the version numbers. Was planning
> to take care of the versioning when there is code diversity on the basis of the
> version number.

That is not how device tree bindings work.  The describe the hardware,
not the driver.

That said, is the block version sufficient to tell whether a given chip
has this erratum?  If so, you don't need a special property for the
erratum.  If not, what is different about the PHY that is not described
by the versioning?

In any case, it would be nice to mention the version register and its
offset in the binding, just so that it becomes part of the definition of
this compatible string, and if we come out with some QorIQ chip with a
USB3 PHY that is totally different and doesn't have that version
register, it'll be clear that it needs a different compatible.

>>> +static inline u32 qoriq_usb3_phy_readl(void __iomem *addr, u32
>>> +offset) {
>>> +	return __raw_readl(addr + offset);
>>> +}
>>> +
>>> +static inline void qoriq_usb3_phy_writel(void __iomem *addr, u32 offset,
>>> +	u32 data)
>>> +{
>>> +	__raw_writel(data, addr + offset);
>>> +}
>>
>> Why raw?  Besides missing barriers, this will cause the accesses to be native-endian
>> which is not correct.
>>
> 
> The only reason for __raw_writel is to make the code faster.

Does that really matter here?

> However, shall I use writel(with both barriers and byte swap) instead 

Yes, if the registers are little-endian on all chips.

> and then make appropriate changes in the value 32'h27672B2A?

Not sure what you mean here.

> In my knowledge, there are more than 5 errata in pipeline,

Then please get all of these errata described in the device tree ASAP
(unless their presence can be reliably inferred from the block version,
as discussed above).

> However, in future, if any other erratum comes up, and it has to be applied
> at any point other than during init, then the variable has to be added in
> qoriq_usb3_phy struct and the property has to be read separately.

Or if the erratum is detected by some means other than a device tree
property...

-Scott

^ permalink raw reply

* Re: [PATCH v4 4/4] ARM: dts: da850: Add the usb otg device nodeg
From: Sekhar Nori @ 2016-11-16  6:36 UTC (permalink / raw)
  To: Bin Liu, Alexandre Bailon, khilman, robh+dt, linux-kernel,
	linux-usb, devicetree, linux-arm-kernel
In-Reply-To: <20161115211951.GH11011@uda0271908>

On Wednesday 16 November 2016 02:49 AM, Bin Liu wrote:
> On Tue, Nov 15, 2016 at 04:16:02PM +0530, Sekhar Nori wrote:
>> On Thursday 03 November 2016 09:29 PM, Alexandre Bailon wrote:
>>> This adds the device tree node for the usb otg
>>> controller present in the da850 family of SoC's.
>>> This also enables the otg usb controller for the lcdk board.
>>>
>>> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
>>> ---
>>>  arch/arm/boot/dts/da850-lcdk.dts |  8 ++++++++
>>>  arch/arm/boot/dts/da850.dtsi     | 15 +++++++++++++++
>>>  2 files changed, 23 insertions(+)
>>>
>>> diff --git a/arch/arm/boot/dts/da850-lcdk.dts b/arch/arm/boot/dts/da850-lcdk.dts
>>> index 7b8ab21..9f5040c 100644
>>> --- a/arch/arm/boot/dts/da850-lcdk.dts
>>> +++ b/arch/arm/boot/dts/da850-lcdk.dts
>>> @@ -158,6 +158,14 @@
>>>  	rx-num-evt = <32>;
>>>  };
>>>  
>>> +&usb_phy {
>>> +	status = "okay";
>>> +	};
>>
>> As mentioned by David already, this node needs to be removed. Please
>> rebase this on top of latest linux-davinci/master when ready for merging
>> (driver changes accepted).
> 
> Dropped this patch due to this comment.

Bin, Please do not apply dts or arch/arm/mach-davinci patches. I have a
bunch queued through my tree and more in pipeline and it will cause
unnecessary merge conflicts in linux-next or at Linus.

For future, I have asked Alexandre to send driver and dts patches as
separate series so there is no confusion on who should apply.

Thanks,
Sekhar

^ permalink raw reply

* [RESEND PATCH v7 3/3] clocksource: Add clockevent support to NPS400 driver
From: Noam Camus @ 2016-11-16  6:31 UTC (permalink / raw)
  To: robh+dt, mark.rutland, daniel.lezcano
  Cc: devicetree, Noam Camus, Vineet.Gupta1, Alexey.Brodkin,
	linux-kernel, tglx, linux-snps-arc
In-Reply-To: <1479277873-18994-1-git-send-email-noamca@mellanox.com>

From: Noam Camus <noamca@mellanox.com>

Till now we used clockevent from generic ARC driver.
This was enough as long as we worked with simple multicore SoC.
When we are working with multithread SoC each HW thread can be
scheduled to receive timer interrupt using timer mask register.
This patch will provide a way to control clock events per HW thread.

The design idea is that for each core there is dedicated register
(TSI) serving all 16 HW threads.
The register is a bitmask with one bit for each HW thread.
When HW thread wants that next expiration of timer interrupt will
hit it then the proper bit should be set in this dedicated register.
When timer expires all HW threads within this core which their bit
is set at the TSI register will be interrupted.

Driver can be used from device tree by:
compatible = "ezchip,nps400-timer0" <-- for clocksource
compatible = "ezchip,nps400-timer1" <-- for clockevent

Note that name convention for timer0/timer1 was taken from legacy
ARC design. This design is our base before adding HW threads.
For backward compatibility we keep "ezchip,nps400-timer" for clocksource

Signed-off-by: Noam Camus <noamca@mellanox.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 .../bindings/timer/ezchip,nps400-timer.txt         |   15 --
 .../bindings/timer/ezchip,nps400-timer0.txt        |   17 ++
 .../bindings/timer/ezchip,nps400-timer1.txt        |   15 ++
 drivers/clocksource/timer-nps.c                    |  170 ++++++++++++++++++++
 4 files changed, 202 insertions(+), 15 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt
 create mode 100644 Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt
 create mode 100644 Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt

diff --git a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt
deleted file mode 100644
index c8c03d7..0000000
--- a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-NPS Network Processor
-
-Required properties:
-
-- compatible :	should be "ezchip,nps400-timer"
-
-Clocks required for compatible = "ezchip,nps400-timer":
-- clocks : Must contain a single entry describing the clock input
-
-Example:
-
-timer {
-	compatible = "ezchip,nps400-timer";
-	clocks = <&sysclk>;
-};
diff --git a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt
new file mode 100644
index 0000000..e3cfce8
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt
@@ -0,0 +1,17 @@
+NPS Network Processor
+
+Required properties:
+
+- compatible :	should be "ezchip,nps400-timer0"
+
+Clocks required for compatible = "ezchip,nps400-timer0":
+- interrupts : The interrupt of the first timer
+- clocks : Must contain a single entry describing the clock input
+
+Example:
+
+timer {
+	compatible = "ezchip,nps400-timer0";
+	interrupts = <3>;
+	clocks = <&sysclk>;
+};
diff --git a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt
new file mode 100644
index 0000000..c0ab419
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt
@@ -0,0 +1,15 @@
+NPS Network Processor
+
+Required properties:
+
+- compatible :	should be "ezchip,nps400-timer1"
+
+Clocks required for compatible = "ezchip,nps400-timer1":
+- clocks : Must contain a single entry describing the clock input
+
+Example:
+
+timer {
+	compatible = "ezchip,nps400-timer1";
+	clocks = <&sysclk>;
+};
diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index 0c8e21f..b4c8a02 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -111,3 +111,173 @@ static int __init nps_setup_clocksource(struct device_node *node)
 
 CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
 		       nps_setup_clocksource);
+CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clk_src, "ezchip,nps400-timer1",
+		       nps_setup_clocksource);
+
+#ifdef CONFIG_EZNPS_MTM_EXT
+#include <soc/nps/mtm.h>
+
+/* Timer related Aux registers */
+#define NPS_REG_TIMER0_TSI	0xFFFFF850
+#define NPS_REG_TIMER0_LIMIT	0x23
+#define NPS_REG_TIMER0_CTRL	0x22
+#define NPS_REG_TIMER0_CNT	0x21
+
+/*
+ * Interrupt Enabled (IE) - re-arm the timer
+ * Not Halted (NH) - is cleared when working with JTAG (for debug)
+ */
+#define TIMER0_CTRL_IE		BIT(0)
+#define TIMER0_CTRL_NH		BIT(1)
+
+static unsigned long nps_timer0_freq;
+static unsigned long nps_timer0_irq;
+
+static void nps_clkevent_rm_thread(void)
+{
+	int thread;
+	unsigned int cflags, enabled_threads;
+
+	hw_schd_save(&cflags);
+
+	enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
+
+	/* remove thread from TSI1 */
+	thread = read_aux_reg(CTOP_AUX_THREAD_ID);
+	enabled_threads &= ~(1 << thread);
+	write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
+
+	/* Acknowledge and if needed re-arm the timer */
+	if (!enabled_threads)
+		write_aux_reg(NPS_REG_TIMER0_CTRL, TIMER0_CTRL_NH);
+	else
+		write_aux_reg(NPS_REG_TIMER0_CTRL,
+			      TIMER0_CTRL_IE | TIMER0_CTRL_NH);
+
+	hw_schd_restore(cflags);
+}
+
+static void nps_clkevent_add_thread(unsigned long delta)
+{
+	int thread;
+	unsigned int cflags, enabled_threads;
+
+	hw_schd_save(&cflags);
+
+	/* add thread to TSI1 */
+	thread = read_aux_reg(CTOP_AUX_THREAD_ID);
+	enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
+	enabled_threads |= (1 << thread);
+	write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
+
+	/* set next timer event */
+	write_aux_reg(NPS_REG_TIMER0_LIMIT, delta);
+	write_aux_reg(NPS_REG_TIMER0_CNT, 0);
+	write_aux_reg(NPS_REG_TIMER0_CTRL,
+		      TIMER0_CTRL_IE | TIMER0_CTRL_NH);
+
+	hw_schd_restore(cflags);
+}
+
+/*
+ * Whenever anyone tries to change modes, we just mask interrupts
+ * and wait for the next event to get set.
+ */
+static int nps_clkevent_set_state(struct clock_event_device *dev)
+{
+	nps_clkevent_rm_thread();
+	disable_percpu_irq(nps_timer0_irq);
+
+	return 0;
+}
+
+static int nps_clkevent_set_next_event(unsigned long delta,
+				       struct clock_event_device *dev)
+{
+	nps_clkevent_add_thread(delta);
+	enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
+
+	return 0;
+}
+
+static DEFINE_PER_CPU(struct clock_event_device, nps_clockevent_device) = {
+	.name				=	"NPS Timer0",
+	.features			=	CLOCK_EVT_FEAT_ONESHOT,
+	.rating				=	300,
+	.set_next_event			=	nps_clkevent_set_next_event,
+	.set_state_oneshot		=	nps_clkevent_set_state,
+	.set_state_oneshot_stopped	=	nps_clkevent_set_state,
+	.set_state_shutdown		=	nps_clkevent_set_state,
+	.tick_resume			=	nps_clkevent_set_state,
+};
+
+static irqreturn_t timer_irq_handler(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+
+	nps_clkevent_rm_thread();
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static int nps_timer_starting_cpu(unsigned int cpu)
+{
+	struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
+
+	evt->cpumask = cpumask_of(smp_processor_id());
+
+	clockevents_config_and_register(evt, nps_timer0_freq, 0, ULONG_MAX);
+	enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
+
+	return 0;
+}
+
+static int nps_timer_dying_cpu(unsigned int cpu)
+{
+	disable_percpu_irq(nps_timer0_irq);
+	return 0;
+}
+
+static int __init nps_setup_clockevent(struct device_node *node)
+{
+	struct clk *clk;
+	int ret;
+
+	nps_timer0_irq = irq_of_parse_and_map(node, 0);
+	if (nps_timer0_irq <= 0) {
+		pr_err("clockevent: missing irq");
+		return -EINVAL;
+	}
+
+	ret = nps_get_timer_clk(node, &nps_timer0_freq, &clk);
+	if (ret)
+		return ret;
+
+	/* Needs apriori irq_set_percpu_devid() done in intc map function */
+	ret = request_percpu_irq(nps_timer0_irq, timer_irq_handler,
+				 "Timer0 (per-cpu-tick)",
+				 &nps_clockevent_device);
+	if (ret) {
+		pr_err("Couldn't request irq\n");
+		clk_disable_unprepare(clk);
+		return ret;
+	}
+
+	ret = cpuhp_setup_state(CPUHP_AP_ARC_TIMER_STARTING,
+				"clockevents/nps:starting",
+				nps_timer_starting_cpu,
+				nps_timer_dying_cpu);
+	if (ret) {
+		pr_err("Failed to setup hotplug state");
+		clk_disable_unprepare(clk);
+		free_percpu_irq(nps_timer0_irq, &nps_clockevent_device);
+		return ret;
+	}
+
+	return 0;
+}
+
+CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clk_evt, "ezchip,nps400-timer0",
+		       nps_setup_clockevent);
+#endif /* CONFIG_EZNPS_MTM_EXT */
-- 
1.7.1

^ permalink raw reply related

* [RESEND PATCH v7 2/3] clocksource: update "fn" at CLOCKSOURCE_OF_DECLARE() of nps400 timer
From: Noam Camus @ 2016-11-16  6:31 UTC (permalink / raw)
  To: robh+dt, mark.rutland, daniel.lezcano
  Cc: tglx, devicetree, linux-kernel, linux-snps-arc, Vineet.Gupta1,
	Alexey.Brodkin, Noam Camus
In-Reply-To: <1479277873-18994-1-git-send-email-noamca@mellanox.com>

From: Noam Camus <noamca@mellanox.com>

nps_setup_clocksource() should take node as only argument as defined by
typedef int (*of_init_fn_1_ret)(struct device_node *)

Therefore need to replace:
int __init nps_setup_clocksource(struct device_node *node, struct clk *clk)
with
int __init nps_setup_clocksource(struct device_node *node)

This patch also serve as preparation for next patch which add support
for clockevents to nps400.
Specifically we add new function nps_get_timer_clk() to serve clocksource
and later clockevent registration.

Signed-off-by: Noam Camus <noamca@mellanox.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/clocksource/timer-nps.c |   65 +++++++++++++++++++++++---------------
 1 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index 70c149a..0c8e21f 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -46,7 +46,35 @@
 /* This array is per cluster of CPUs (Each NPS400 cluster got 256 CPUs) */
 static void *nps_msu_reg_low_addr[NPS_CLUSTER_NUM] __read_mostly;
 
-static unsigned long nps_timer_rate;
+static int __init nps_get_timer_clk(struct device_node *node,
+			     unsigned long *timer_freq,
+			     struct clk **clk)
+{
+	int ret;
+
+	*clk = of_clk_get(node, 0);
+	if (IS_ERR(*clk)) {
+		pr_err("timer missing clk");
+		return PTR_ERR(*clk);
+	}
+
+	ret = clk_prepare_enable(*clk);
+	if (ret) {
+		pr_err("Couldn't enable parent clk\n");
+		clk_put(*clk);
+		return ret;
+	}
+
+	*timer_freq = clk_get_rate(*clk);
+	if (!(*timer_freq)) {
+		pr_err("Couldn't get clk rate\n");
+		clk_disable_unprepare(*clk);
+		clk_put(*clk);
+		return -EINVAL;
+	}
+
+	return 0;
+}
 
 static cycle_t nps_clksrc_read(struct clocksource *clksrc)
 {
@@ -55,26 +83,24 @@ static cycle_t nps_clksrc_read(struct clocksource *clksrc)
 	return (cycle_t)ioread32be(nps_msu_reg_low_addr[cluster]);
 }
 
-static int __init nps_setup_clocksource(struct device_node *node,
-					struct clk *clk)
+static int __init nps_setup_clocksource(struct device_node *node)
 {
 	int ret, cluster;
+	struct clk *clk;
+	unsigned long nps_timer1_freq;
+
 
 	for (cluster = 0; cluster < NPS_CLUSTER_NUM; cluster++)
 		nps_msu_reg_low_addr[cluster] =
 			nps_host_reg((cluster << NPS_CLUSTER_OFFSET),
-				 NPS_MSU_BLKID, NPS_MSU_TICK_LOW);
+				     NPS_MSU_BLKID, NPS_MSU_TICK_LOW);
 
-	ret = clk_prepare_enable(clk);
-	if (ret) {
-		pr_err("Couldn't enable parent clock\n");
+	ret = nps_get_timer_clk(node, &nps_timer1_freq, &clk);
+	if (ret)
 		return ret;
-	}
 
-	nps_timer_rate = clk_get_rate(clk);
-
-	ret = clocksource_mmio_init(nps_msu_reg_low_addr, "EZnps-tick",
-				    nps_timer_rate, 301, 32, nps_clksrc_read);
+	ret = clocksource_mmio_init(nps_msu_reg_low_addr, "nps-tick",
+				    nps_timer1_freq, 300, 32, nps_clksrc_read);
 	if (ret) {
 		pr_err("Couldn't register clock source.\n");
 		clk_disable_unprepare(clk);
@@ -83,18 +109,5 @@ static int __init nps_setup_clocksource(struct device_node *node,
 	return ret;
 }
 
-static int __init nps_timer_init(struct device_node *node)
-{
-	struct clk *clk;
-
-	clk = of_clk_get(node, 0);
-	if (IS_ERR(clk)) {
-		pr_err("Can't get timer clock.\n");
-		return PTR_ERR(clk);
-	}
-
-	return nps_setup_clocksource(node, clk);
-}
-
 CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
-		       nps_timer_init);
+		       nps_setup_clocksource);
-- 
1.7.1

^ permalink raw reply related

* [RESEND PATCH v7 1/3] soc: Support for NPS HW scheduling
From: Noam Camus @ 2016-11-16  6:31 UTC (permalink / raw)
  To: robh+dt, mark.rutland, daniel.lezcano
  Cc: devicetree, Noam Camus, Vineet.Gupta1, Alexey.Brodkin,
	linux-kernel, tglx, linux-snps-arc
In-Reply-To: <1479277873-18994-1-git-send-email-noamca@mellanox.com>

From: Noam Camus <noamca@mellanox.com>

This new header file is for NPS400 SoC (part of ARC architecture).
The header file includes macros for save/restore of HW scheduling.
The control of HW scheduling is achieved by writing core registers.
This code was moved from arc/plat-eznps so it can be used
from drivers/clocksource/, available only for CONFIG_EZNPS_MTM_EXT.

Signed-off-by: Noam Camus <noamca@mellanox.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 arch/arc/plat-eznps/include/plat/ctop.h |    2 -
 include/soc/nps/mtm.h                   |   59 +++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 2 deletions(-)
 create mode 100644 include/soc/nps/mtm.h

diff --git a/arch/arc/plat-eznps/include/plat/ctop.h b/arch/arc/plat-eznps/include/plat/ctop.h
index 9d6718c..ee2e32d 100644
--- a/arch/arc/plat-eznps/include/plat/ctop.h
+++ b/arch/arc/plat-eznps/include/plat/ctop.h
@@ -46,9 +46,7 @@
 #define CTOP_AUX_UDMC				(CTOP_AUX_BASE + 0x300)
 
 /* EZchip core instructions */
-#define CTOP_INST_HWSCHD_OFF_R3			0x3B6F00BF
 #define CTOP_INST_HWSCHD_OFF_R4			0x3C6F00BF
-#define CTOP_INST_HWSCHD_RESTORE_R3		0x3E6F70C3
 #define CTOP_INST_HWSCHD_RESTORE_R4		0x3E6F7103
 #define CTOP_INST_SCHD_RW			0x3E6F7004
 #define CTOP_INST_SCHD_RD			0x3E6F7084
diff --git a/include/soc/nps/mtm.h b/include/soc/nps/mtm.h
new file mode 100644
index 0000000..d2f5e7e
--- /dev/null
+++ b/include/soc/nps/mtm.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef SOC_NPS_MTM_H
+#define SOC_NPS_MTM_H
+
+#define CTOP_INST_HWSCHD_OFF_R3                 0x3B6F00BF
+#define CTOP_INST_HWSCHD_RESTORE_R3             0x3E6F70C3
+
+static inline void hw_schd_save(unsigned int *flags)
+{
+	__asm__ __volatile__(
+	"       .word %1\n"
+	"       st r3,[%0]\n"
+	:
+	: "r"(flags), "i"(CTOP_INST_HWSCHD_OFF_R3)
+	: "r3", "memory");
+}
+
+static inline void hw_schd_restore(unsigned int flags)
+{
+	__asm__ __volatile__(
+	"       mov r3, %0\n"
+	"       .word %1\n"
+	:
+	: "r"(flags), "i"(CTOP_INST_HWSCHD_RESTORE_R3)
+	: "r3");
+}
+
+#endif /* SOC_NPS_MTM_H */
-- 
1.7.1

^ permalink raw reply related

* [RESEND PATCH v7 0/3] Add clockevent for timer-nps driver to NPS400 SoC
From: Noam Camus @ 2016-11-16  6:31 UTC (permalink / raw)
  To: robh+dt, mark.rutland, daniel.lezcano
  Cc: tglx, devicetree, linux-kernel, linux-snps-arc, Vineet.Gupta1,
	Alexey.Brodkin, Noam Camus

From: Noam Camus <noamca@mellanox.com>

Change log
---
V6 --> V7
Apply several comments made by Daniel Lezcano:
1) Remove CLOCK_EVT_FEAT_PERIODIC support. This way it is
 pure oneshot driver. This simplifies driver so that:
 nps_clkevent_add_thread()
 nps_clkevent_rm_thread()
 are more clearer without any vague logic if to change
 TSI bit of current HW thread or not.
2) tick_resume is also calls nps_clkevent_rm_thread()
3) Few (hopefully last) typo fixes. 

V5 --> V6
Apply several comments made by Daniel Lezcano:
1) nps_get_timer_clk() - use clk_put() on error scenario
2) nps_get_timer_clk() - return EINVAL and not plain 1
3) Fix typos in log (double checked with spell checker)

V4 --> V5
Apply several comments made by Daniel Lezcano:
1) Add __init attribute to nps_get_timer_clk()
2) Fix return value of nps_get_timer_clk()
 when failing to get clk rate
3) Change clocksource rate from 301 -> 300

V3 --> V4
Main changes are [Thanks for the review]:
Fix many typos at log [Daniel]
Add handling for bad return values [Daniel and Thomas]
Replace use of internal irqchip pointers with existing IRQ API [Thomas]
Provide interrupt handler (percpu) with dev_id equal to evt [Thomas]
Fix passing *clk by reference to nps_get_timer_clk() [Daniel]

V2 --> V3
Apply Rob Herring comment about backword compatibility

V1 --> V2
Apply Daniel Lezcano comments:
	CLOCKSOURCE_OF_DECLARE return value
	update hotplug callbacks usage
	squash of 2 first commits.
In this version I created new commit to serve as preperation for adding clockevents.
This way the last patch is more readable with clockevent content.
---

In first version of this driver we supported clocksource for the NPS400.
The support for clockevent was taken from Synopsys ARC timer driver.
This was good for working with our simulator of NPS400.
However in NPS400 ASIC the timers behave differently than simulation.
The timers in ASIC are shared between all threads whithin a core
and hence need different driver to support this behaviour.

The idea of this design is that we got 16 HW threads per core
each represented at bimask in a shared register in this core.
So when thread wants that next clockevent expiration will produce
timer interrupt to itself the correspondance bit in this register
should be set.
So theoretically if all 16 bits are set then all HW threads will get
timer interrupt on next expiration of timer 0.

Note that we use Synopsys ARC design naming convention for the timers
where:
timer0 is used for clockevents
timer1 is used for clocksource.

Noam Camus (3):
  soc: Support for NPS HW scheduling
  clocksource: update "fn" at CLOCKSOURCE_OF_DECLARE() of nps400 timer
  clocksource: Add clockevent support to NPS400 driver

 .../bindings/timer/ezchip,nps400-timer.txt         |   15 --
 .../bindings/timer/ezchip,nps400-timer0.txt        |   17 ++
 .../bindings/timer/ezchip,nps400-timer1.txt        |   15 ++
 arch/arc/plat-eznps/include/plat/ctop.h            |    2 -
 drivers/clocksource/timer-nps.c                    |  223 ++++++++++++++++++--
 include/soc/nps/mtm.h                              |   59 +++++
 6 files changed, 294 insertions(+), 37 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt
 create mode 100644 Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt
 create mode 100644 Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt
 create mode 100644 include/soc/nps/mtm.h

^ permalink raw reply

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
From: Anurup M @ 2016-11-16  5:54 UTC (permalink / raw)
  To: Mark Rutland
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm
In-Reply-To: <20161115095148.GA29104@leverpostej>



On Tuesday 15 November 2016 03:21 PM, Mark Rutland wrote:
> On Mon, Nov 14, 2016 at 05:36:44AM +0530, Anurup M wrote:
>> On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
>>> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>>>> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
>>>> +		   or IO die in the chip.
>>> What's this needed for?
>> This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2
>> - for scl-id = 2).
>> This is to identify the pmu correspond to which CPU die in the socket.
>>>> +	- num-events : No of events supported by this PMU device.
>>>> +
>>>> +	- num-counters : No of hardware counters available for counting.
>>> This isn't probeable or well-known?
>> My idea is to have the common properties of SoC PMU added here.
>> The num-events, num-counters etc. So that handling can be made
>> common in the driver.
>> Is it not recommended? Please share your comments.
> This feels like something that should be well-known for the programming
> model of the device. If the number of events and/or counters shange, I'd
> expect other things to also change such that the device is no longer
> compatible with previous versions.
>
> [...]
>
Agreed, it is possible that the versions can be still incompatible. 
Shall move it to
driver in v2.

>> The below two properties (module-id, cfgen-map) differs between
>> chips hip05/06 and hip07.
> The module-id property sounds like a HW description, but it's not
> entirely clear to me what cfgen-map is; more comments on that below.
>
>> Please suggest.
>>>> +	- module-id : Module ID to input for djtag. This property is an array of
>>>> +		      module_id for each L3 cache banks.
>>>> +
>>>> +	- num-banks : Number of banks or instances of the device.
>>> What's a bank? Surely they have separate instances of the PMU?
>> Yes each bank is a separate instance of PMU.
>> If it is recommended to have each L3 cache bank registered as
>> separate PMU with perf, then this property will be removed.
> Generally, I think that separate instances are preferable.
>
>>> What order are these in?
>> The bank number will start from "1" till "4" for L3 cache as there
>> are four banks in hip05/06/07 chips.
>>>> +	- cfgen-map : Config enable array to select the bank.
>>> Huh?
> As above, it's not clear to me what this property represents. Could you
> please clarify?
This property is used to select the bank. The naming lead to confusion.
I shall change it to bank_select.
If it is recommended to register each L3 cache bank as separate PMU then
this property will be moved to driver.

Thanks
Anurup
>
> Thanks,
> Mark.


^ permalink raw reply

* RE: [PATCH 1/2] drivers: usb: phy: Add qoriq usb 3.0 phy driver support
From: Sriram Dash @ 2016-11-16  5:48 UTC (permalink / raw)
  To: Rob Herring
  Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
	felipe.balbi@linux.intel.com, mathias.nyman@intel.com,
	catalin.marinas@arm.com, linux-usb@vger.kernel.org,
	will.deacon@arm.com, linux-kernel@vger.kernel.org, kishon@ti.com,
	stern@rowland.harvard.edu, Suresh Gupta,
	gregkh@linuxfoundation.org, pku.leo@gmail.com,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <20161116000740.iq6ffcjanrrvrdnq@rob-hp-laptop>

>From: Rob Herring [mailto:robh@kernel.org]
>On Mon, Nov 14, 2016 at 10:56:54AM +0530, Sriram Dash wrote:
>> Adds qoriq usb 3.0 phy driver support for LS1043A platform.
>> Describes the qoriq usb 2.0 phy driver binding, currently used for
>> LS1043A platform.
>>
>> Signed-off-by: Sriram Dash <sriram.dash@nxp.com>
>> ---
>>  .../devicetree/bindings/phy/phy-qoriq-usb3.txt     |  36 ++++
>>  drivers/phy/Kconfig                                |   8 +
>>  drivers/phy/Makefile                               |   1 +
>>  drivers/phy/phy-qoriq-usb3.c                       | 202 +++++++++++++++++++++
>>  4 files changed, 247 insertions(+)
>>  create mode 100644
>> Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>>  create mode 100644 drivers/phy/phy-qoriq-usb3.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>> b/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>> new file mode 100644
>> index 0000000..d934c80
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>> @@ -0,0 +1,36 @@
>> +Driver for Freescale USB 3.0 PHY
>> +
>> +Required properties:
>> +
>> +- compatible :	fsl,qoriq-usb3-phy
>> +- reg :		register mappings for Parameter Configuration Register
>> +		and Phy base offset.
>> +- reg-names :	"param_ctrl" and "phy_base"
>> +- phy_type :	For multi port host USB controllers, should be one of
>> +		"ulpi", or "serial". For dual role USB controllers,
>> +		should be one of "ulpi", "utmi", "utmi_wide", or "serial".

Hi Rob,

>
>Do any of these really apply to a USB3 PHY?
>

The concerned USB3 phy used is UTMI. I agree to your point somewhat that
all the types are not required now. Anyway, shall I make it an optional
property, with the mention of only UTMI and ULPI?

>Rob
>
>> +
>> +Example:
>> +		usbphy0: usb3-phy@084F0000 {
>
>usb-phy@...
>

Ok. Will change in the next rev for Documentation and dts (patch 2/2)

>> +                        compatible = "fsl,qoriq-usb3-phy";
>> +                        reg = <0x0 0x01570070 0x0 0xC>, <0x0 0x084F0000 0x0 0x5000>;
>> +                        reg-names = "param_ctrl", "phy_base";
>> +                        #phy-cells = <0>;
>> +                        phy_type = "utmi";
>> +                };
>> +
>> +                usbphy1: usb3-phy@08500000 {
>> +                        compatible = "fsl,qoriq-usb3-phy";
>> +                        reg = <0x0 0x0157007C 0x0 0xC>, <0x0 0x08500000 0x0 0x5000>;
>> +                        reg-names = "param_ctrl", "phy_base";
>> +                        #phy-cells = <0>;
>> +                        phy_type = "utmi";
>> +                };
>> +
>> +                usbphy2: usb3-phy@08510000 {
>> +                        compatible = "fsl,qoriq-usb3-phy";
>> +                        reg = <0x0 0x01570088 0x0 0xC>, <0x0 0x08510000 0x0 0x5000>;
>> +                        reg-names = "param_ctrl", "phy_base";
>> +                        #phy-cells = <0>;
>> +                        phy_type = "utmi";
>> +                };

^ permalink raw reply

* Re: [alsa-devel] [PATCH v2] clkdev: add devm_of_clk_get()
From: Kuninori Morimoto @ 2016-11-16  5:17 UTC (permalink / raw)
  To: Michael Turquette, Rob Herring, Russell King
  Cc: Mark Brown, Linux-ALSA, Linux-DT, Linux-Kernel, linux-clk,
	Linux-ARM
In-Reply-To: <878twndi54.wl%kuninori.morimoto.gx@renesas.com>


Hi Rob, Michael, Russell


What is the conclusion of this patch ?
We shouldn't add devm_of_clk_get() ? or can I continue ?

The problem of current [devm_]clk_get() handles *dev only,
but I need to get clocks from DT node, not dev

	sound_soc {
		...
		cpu {
			...
=>			clocks = <&xxx>;
		};
		codec {
			...
=>			clocks = <&xxx>;
		};
	};

> > Thank you for your feedback
> > 
> > > > struct clk *clk_get(struct device *dev, const char *con_id)
> > > > {
> > > >         ...
> > > >         if (dev) {
> > > >                 clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
> > > >                                            ~~~~~~~~~~~~
> > > >                 ...
> > > >         }
> > > > }
> > > > 
> > > > I would like to select specific device_node.
> > > 
> > > Do you have access to the struct device that you want to target? Can you
> > > pass that device into either clk_get or devm_clk_get?
> > 
> > If my understanding was correct, I think I can't.
> > In below case, "sound_soc" has its *dev, but "cpu" and "codec" doesn't
> > have *dev, it has node only. Thus, we are using of_clk_get() for these now.
> > 
> > 	clk = of_clk_get(cpu, xxx);
> > 	clk = of_clk_get(codec, xxx);
> > 
> > 	sound_soc {
> > 		...
> > 		cpu {
> > 			...
> > =>			clocks = <&xxx>;
> > 		};
> > 		codec {
> > 			...
> > =>			clocks = <&xxx>;
> > 		};
> > 	};


Best regards
---
Kuninori Morimoto

^ permalink raw reply

* Re: [PATCH v7 08/14] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
From: Ritesh Harjani @ 2016-11-16  4:42 UTC (permalink / raw)
  To: Stephen Boyd, adrian.hunter, ulf.hansson
  Cc: linux-mmc, shawn.lin, andy.gross, devicetree, linux-clk,
	david.brown, linux-arm-msm, georgi.djakov, alex.lemberg,
	mateusz.nowak, Yuliy.Izrailov, asutoshd, kdorfman, david.griego,
	stummala, venkatg, rnayak, pramod.gurav
In-Reply-To: <3c1a7c72-0ac1-8ed0-87fc-238331f0645b@codeaurora.org>

Hi,

On 11/15/2016 10:40 AM, Ritesh Harjani wrote:
> Hi Stephen/Adrian,
>
> On 11/15/2016 1:07 AM, Stephen Boyd wrote:
>> On 11/14, Ritesh Harjani wrote:
>>> @@ -577,6 +578,90 @@ static unsigned int
>>> sdhci_msm_get_min_clock(struct sdhci_host *host)
>>>      return SDHCI_MSM_MIN_CLOCK;
>>>  }
>>>
>>> +/**
>>> + * __sdhci_msm_set_clock - sdhci_msm clock control.
>>> + *
>>> + * Description:
>>> + * Implement MSM version of sdhci_set_clock.
>>> + * This is required since MSM controller does not
>>> + * use internal divider and instead directly control
>>> + * the GCC clock as per HW recommendation.
>>> + **/
>>> +void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
>>> +{
>>> +    u16 clk;
>>> +    unsigned long timeout;
>>> +
>>> +    /*
>>> +     * Keep actual_clock as zero -
>>> +     * - since there is no divider used so no need of having
>>> actual_clock.
>>> +     * - MSM controller uses SDCLK for data timeout calculation. If
>>> +     *   actual_clock is zero, host->clock is taken for calculation.
>>> +     */
>>> +    host->mmc->actual_clock = 0;
>>> +
>>> +    sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
>>> +
>>> +    if (clock == 0)
>>> +        return;
>>> +
>>> +    /*
>>> +     * MSM controller do not use clock divider.
>>> +     * Thus read SDHCI_CLOCK_CONTROL and only enable
>>> +     * clock with no divider value programmed.
>>> +     */
>>> +    clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
>>> +
>>> +    clk |= SDHCI_CLOCK_INT_EN;
>>> +    sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>>> +
>>> +    /* Wait max 20 ms */
>>> +    timeout = 20;
>>> +    while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
>>> +        & SDHCI_CLOCK_INT_STABLE)) {
>>> +        if (timeout == 0) {
>>> +            pr_err("%s: Internal clock never stabilised\n",
>>> +                   mmc_hostname(host->mmc));
>>> +            return;
>>> +        }
>>> +        timeout--;
>>> +        mdelay(1);
>>> +    }
>>> +
>>> +    clk |= SDHCI_CLOCK_CARD_EN;
>>> +    sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>>
>> This is almost a copy/paste of sdhci_set_clock(). Can we make
>> sdhci_set_clock() call a __sdhci_set_clock() function that takes
>> unsigned int clock, and also a flag indicating if we want to set
>> the internal clock divider or not? Then we can call
>> __sdhci_set_clock() from sdhci_set_clock() with (clock, true) as
>> arguments and (clock, false).
Actually what you may be referring here is some sort of quirks which is 
not entertained any more for sdhci driver.
sdhci is tending towards becoming a library and hence such changes are 
restricted.

But I think we may do below changes to avoid duplication of code which 
enables the sdhci internal clock and waits for internal clock to be stable.

Adrian, could you please tell if this should be ok?
Then we may be able to call for sdhci_set_clock_enable function from
sdhci_msm_set_clock.


diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 42ef3eb..28e605c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1343,19 +1343,8 @@ u16 sdhci_calc_clk(struct sdhci_host *host, 
unsigned int clock,
  }
  EXPORT_SYMBOL_GPL(sdhci_calc_clk);

-void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+void sdhci_set_clock_enable(struct sdhci_host *host, unsigned short clk)
  {
-       u16 clk;
-       unsigned long timeout;
-
-       host->mmc->actual_clock = 0;
-
-       sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
-
-       if (clock == 0)
-               return;
-
-       clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);

         clk |= SDHCI_CLOCK_INT_EN;
         sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
@@ -1377,6 +1366,24 @@ void sdhci_set_clock(struct sdhci_host *host, 
unsigned int clock)
         clk |= SDHCI_CLOCK_CARD_EN;
         sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
  }
+EXPORT_SYMBOL_GPL(sdhci_set_clock_enable);
+
+void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       u16 clk;
+       unsigned long timeout;
+
+       host->mmc->actual_clock = 0;
+
+       sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+       if (clock == 0)
+               return;
+
+       clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
+
+       sdhci_set_clock_enable(host, clk);
+}
  EXPORT_SYMBOL_GPL(sdhci_set_clock);

  static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char 
mode,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 766df17..43382e1 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -681,6 +681,7 @@ static inline bool sdhci_sdio_irq_enabled(struct 
sdhci_host *host)
  u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
                    unsigned int *actual_clock);
  void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
+void sdhci_set_clock_enable(struct sdhci_host *host, unsigned short clk);
  void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
                      unsigned short vdd);
  void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,



> Adrian,
> Could you please comment here ?
>
>>
>>> +}
>>> +
>>> +/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
>>> +static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned
>>> int clock)
>>> +{
>>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>> +    struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
>>> +    int rc;
>>> +
>>> +    if (!clock) {
>>> +        msm_host->clk_rate = clock;
>>> +        goto out;
>>> +    }
>>> +
>>> +    spin_unlock_irq(&host->lock);
>>> +    if (clock != msm_host->clk_rate) {
>>
>> Why do we need to check here? Can't we call clk_set_rate()
>> Unconditionally?
> Since it may so happen that above layers may call for ->set_clock
> function with same requested clock more than once, hence we cache the
> host->clock here.
> Also, since requested clock (host->clock) can be say 400Mhz but the
> actual pltfm supported clock would be say 384MHz.
>
>
>>
>>> +        rc = clk_set_rate(msm_host->clk, clock);
>>> +        if (rc) {
>>> +            pr_err("%s: Failed to set clock at rate %u\n",
>>> +                   mmc_hostname(host->mmc), clock);
>>> +            spin_lock_irq(&host->lock);
>>> +            goto out;
>>
>> Or replace the above two lines with goto err;
> Ok, I will have another label out_lock instead of err.
>>
>>> +        }
>>> +        msm_host->clk_rate = clock;
>>> +        pr_debug("%s: Setting clock at rate %lu\n",
>>> +             mmc_hostname(host->mmc), clk_get_rate(msm_host->clk));
>>> +    }
>>
>> And put an err label here.
> will put the label here as out_lock;
>>
>>> +    spin_lock_irq(&host->lock);
>>> +out:
>>> +    __sdhci_msm_set_clock(host, clock);
>>> +}
>>> +
>>>  static const struct of_device_id sdhci_msm_dt_match[] = {
>>>      { .compatible = "qcom,sdhci-msm-v4" },
>>>      {},
>>
>

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply related

* Re: [PATCH v5] drm/mediatek: fixed the calc method of data rate per lane
From: Daniel Kurtz @ 2016-11-16  4:31 UTC (permalink / raw)
  To: Jitao Shi
  Cc: Philipp Zabel, CK Hu (胡俊光), Matthias Brugger,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Ajay Kumar, Inki Dae, Rahul Sharma, Sean Paul, Vincent Palatin,
	Andy Yan, Russell King, open list:OPEN FIRMWARE AND...,
	linux-kernel@vger.kernel.org, dri-devel
In-Reply-To: <1479266454-31892-1-git-send-email-jitao.shi@mediatek.com>

Hi Jitao,

On Wed, Nov 16, 2016 at 11:20 AM, Jitao Shi <jitao.shi@mediatek.com> wrote:
>
> Tune dsi frame rate by pixel clock, dsi add some extra signal (i.e.
> Tlpx, Ths-prepare, Ths-zero, Ths-trail,Ths-exit) when enter and exit LP
> mode, those signals will cause h-time larger than normal and reduce FPS.
> So need to multiply a coefficient to offset the extra signal's effect.
>   coefficient = ((htotal*bpp/lane_number)+Tlpx+Ths_prep+Ths_zero+
>                  Ths_trail+Ths_exit)/(htotal*bpp/lane_number)
>
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>

For this patch,
Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>

But, one more clean up suggestion for another patch, below...

> ---
> Change since v4:
>  - tune the calc comment more clear.
>  - define the phy timings as constants.
>
> Chnage since v3:
>  - wrapp the commit msg.
>  - fix alignment of some lines.
>
> Change since v2:
>  - move phy timing back to dsi_phy_timconfig.
>
> Change since v1:
>  - phy_timing2 and phy_timing3 refer clock cycle time.
>  - define values of LPX HS_PRPR HS_ZERO HS_TRAIL TA_GO TA_SURE TA_GET DA_HS_EXIT.
> ---
>  drivers/gpu/drm/mediatek/mtk_dsi.c |   64 +++++++++++++++++++++++++++---------
>  1 file changed, 48 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
> index 28b2044..eaa5a22 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dsi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
> @@ -86,7 +86,7 @@
>
>  #define DSI_PHY_TIMECON0       0x110
>  #define LPX                            (0xff << 0)
> -#define HS_PRPR                                (0xff << 8)
> +#define HS_PREP                                (0xff << 8)
>  #define HS_ZERO                                (0xff << 16)
>  #define HS_TRAIL                       (0xff << 24)
>
> @@ -102,10 +102,16 @@
>  #define CLK_TRAIL                      (0xff << 24)
>
>  #define DSI_PHY_TIMECON3       0x11c
> -#define CLK_HS_PRPR                    (0xff << 0)
> +#define CLK_HS_PREP                    (0xff << 0)
>  #define CLK_HS_POST                    (0xff << 8)
>  #define CLK_HS_EXIT                    (0xff << 16)
>
> +#define T_LPX          5
> +#define T_HS_PREP      6
> +#define T_HS_TRAIL     8
> +#define T_HS_EXIT      7
> +#define T_HS_ZERO      10
> +
>  #define NS_TO_CYCLE(n, c)    ((n) / (c) + (((n) % (c)) ? 1 : 0))
>
>  struct phy;
> @@ -161,20 +167,18 @@ static void mtk_dsi_mask(struct mtk_dsi *dsi, u32 offset, u32 mask, u32 data)
>  static void dsi_phy_timconfig(struct mtk_dsi *dsi)
>  {
>         u32 timcon0, timcon1, timcon2, timcon3;
> -       unsigned int ui, cycle_time;
> -       unsigned int lpx;
> +       u32 ui, cycle_time;
>
>         ui = 1000 / dsi->data_rate + 0x01;
>         cycle_time = 8000 / dsi->data_rate + 0x01;
> -       lpx = 5;
>
> -       timcon0 = (8 << 24) | (0xa << 16) | (0x6 << 8) | lpx;
> -       timcon1 = (7 << 24) | (5 * lpx << 16) | ((3 * lpx) / 2) << 8 |
> -                 (4 * lpx);
> +       timcon0 = T_LPX | T_HS_PREP << 8 | T_HS_ZERO << 16 | T_HS_TRAIL << 24;
> +       timcon1 = 4 * T_LPX | (3 * T_LPX / 2) << 8 | 5 * T_LPX << 16 |
> +                 T_HS_EXIT << 24;
>         timcon2 = ((NS_TO_CYCLE(0x64, cycle_time) + 0xa) << 24) |
>                   (NS_TO_CYCLE(0x150, cycle_time) << 16);
> -       timcon3 = (2 * lpx) << 16 | NS_TO_CYCLE(80 + 52 * ui, cycle_time) << 8 |
> -                  NS_TO_CYCLE(0x40, cycle_time);
> +       timcon3 = NS_TO_CYCLE(0x40, cycle_time) | (2 * T_LPX) << 16 |
> +                 NS_TO_CYCLE(80 + 52 * ui, cycle_time) << 8;
>
>         writel(timcon0, dsi->regs + DSI_PHY_TIMECON0);
>         writel(timcon1, dsi->regs + DSI_PHY_TIMECON1);
> @@ -202,19 +206,47 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
>  {
>         struct device *dev = dsi->dev;
>         int ret;
> +       u64 pixel_clock, total_bits;
> +       u32 htotal, htotal_bits, bit_per_pixel, overhead_cycles, overhead_bits;
>
>         if (++dsi->refcount != 1)
>                 return 0;
>
> +       switch (dsi->format) {
> +       case MIPI_DSI_FMT_RGB565:
> +               bit_per_pixel = 16;
> +               break;
> +       case MIPI_DSI_FMT_RGB666_PACKED:
> +               bit_per_pixel = 18;
> +               break;
> +       case MIPI_DSI_FMT_RGB666:
> +       case MIPI_DSI_FMT_RGB888:
> +       default:
> +               bit_per_pixel = 24;
> +               break;
> +       }
> +
>         /**
> -        * data_rate = (pixel_clock / 1000) * pixel_dipth * mipi_ratio;
> -        * pixel_clock unit is Khz, data_rata unit is MHz, so need divide 1000.
> -        * mipi_ratio is mipi clk coefficient for balance the pixel clk in mipi.
> -        * we set mipi_ratio is 1.05.
> +        * vm.pixelclock is in kHz, pixel_clock unit is Hz, so multiply by 1000
> +        * htotal_time = htotal * byte_per_pixel / num_lanes
> +        * overhead_time = lpx + hs_prepare + hs_zero + hs_trail + hs_exit
> +        * mipi_ratio = (htotal_time + overhead_time) / htotal_time
> +        * data_rate = pixel_clock * bit_per_pixel * mipi_ratio / num_lanes;
>          */
> -       dsi->data_rate = dsi->vm.pixelclock * 3 * 21 / (1 * 1000 * 10);
> +       pixel_clock = dsi->vm.pixelclock * 1000;
> +       htotal = dsi->vm.hactive + dsi->vm.hback_porch + dsi->vm.hfront_porch +
> +                       dsi->vm.hsync_len;
> +       htotal_bits = htotal * bit_per_pixel;
> +
> +       overhead_cycles = T_LPX + T_HS_PREP + T_HS_ZERO + T_HS_TRAIL +
> +                       T_HS_EXIT;
> +       overhead_bits = overhead_cycles * dsi->lanes * 8;
> +       total_bits = htotal_bits + overhead_bits;
> +
> +       dsi->data_rate = DIV_ROUND_UP_ULL(pixel_clock * total_bits,
> +                                         htotal * dsi->lanes);

dsi->data_rate is only used here and in dsi_phy_timconfig(), which is
called directly a few lines down in this same function.
Rather than permanently storing this as state in struct mtk_dsi, just
pass the newly computed data_rate as a paratmer to
dsi_phy_timconfig().

Thanks,
-Dan

>
> -       ret = clk_set_rate(dsi->hs_clk, dsi->data_rate * 1000000);
> +       ret = clk_set_rate(dsi->hs_clk, dsi->data_rate);
>         if (ret < 0) {
>                 dev_err(dev, "Failed to set data rate: %d\n", ret);
>                 goto err_refcount;
> --
> 1.7.9.5
>

^ permalink raw reply

* Re: [PATCH v10 01/11] remoteproc: st_slim_rproc: add a slimcore rproc driver
From: Vinod Koul @ 2016-11-16  3:32 UTC (permalink / raw)
  To: Peter Griffin
  Cc: Bjorn Andersson,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, ohad-Ix1uc/W3ht7QT0dZR+AlfA,
	patrice.chotard-qxv4g6HH51o, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	dmaengine-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-remoteproc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161114114216.GA8986@griffinp-ThinkPad-X1-Carbon-2nd>

On Mon, Nov 14, 2016 at 11:42:16AM +0000, Peter Griffin wrote:
> Hi Vinod,
> 
> On Mon, 14 Nov 2016, Vinod Koul wrote:
> 
> > On Mon, Nov 07, 2016 at 01:57:35PM +0000, Peter Griffin wrote:
> > > > 
> > > > As you now make changes to the entire remoteproc Kconfig file, rather
> > > > than simply add a Kconfig symbol we can't bring this in via Vinod's tree
> > > > without providing Linus with a messy merge conflict.
> > > > 
> > > > So the remoteproc parts now has to go through my tree.
> > > 
> > > OK, I think the best approach is for Vinod to create an immutable
> > > branch with the entire fdma series on, and then both of you merge that branch into
> > > your respective trees.
> > 
> > my topic/st_fdma is immutable branch. You cna merge it, if you need a signed
> > tag, please do let me know
> 
> OK.
> 
> > 
> > > 
> > > That way there won't be any conflicts and you can both accept further changes
> > > for v4.9 release. Trying to take half the series via rproc, and half via dma trees won't work
> > > because they have dependencies on each other.
> > > 
> > > I will send a v11 series in a moment which includes the feedback in this email
> > > and also include the additional fixes which Vinod has applied since the driver
> > > has been in linux-next.
> > 
> > WHY.. Stuff is already merged twice!
> 
> When the feedback is "there is an unrelated change in this patch", the only way
> you can fix that is by having a new version of the patch.

It can be reverted and clean patch applied..

-- 
~Vinod
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v5] drm/mediatek: fixed the calc method of data rate per lane
From: Jitao Shi @ 2016-11-16  3:20 UTC (permalink / raw)
  To: Philipp Zabel, ck.hu, Matthias Brugger
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jitao Shi, Ajay Kumar, Inki Dae, Rahul Sharma, Sean Paul,
	Vincent Palatin, Andy Yan, Russell King, devicetree, linux-kernel,
	dri-devel, linux-arm-kernel, linux-mediatek, srv_heupstream,
	Sascha Hauer, yingjoe.chen

Tune dsi frame rate by pixel clock, dsi add some extra signal (i.e.
Tlpx, Ths-prepare, Ths-zero, Ths-trail,Ths-exit) when enter and exit LP
mode, those signals will cause h-time larger than normal and reduce FPS.
So need to multiply a coefficient to offset the extra signal's effect.
  coefficient = ((htotal*bpp/lane_number)+Tlpx+Ths_prep+Ths_zero+
		 Ths_trail+Ths_exit)/(htotal*bpp/lane_number)

Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
---
Change since v4:
 - tune the calc comment more clear.
 - define the phy timings as constants.

Chnage since v3:
 - wrapp the commit msg.
 - fix alignment of some lines. 

Change since v2:
 - move phy timing back to dsi_phy_timconfig.

Change since v1:
 - phy_timing2 and phy_timing3 refer clock cycle time.
 - define values of LPX HS_PRPR HS_ZERO HS_TRAIL TA_GO TA_SURE TA_GET DA_HS_EXIT.
---
 drivers/gpu/drm/mediatek/mtk_dsi.c |   64 +++++++++++++++++++++++++++---------
 1 file changed, 48 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index 28b2044..eaa5a22 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -86,7 +86,7 @@
 
 #define DSI_PHY_TIMECON0	0x110
 #define LPX				(0xff << 0)
-#define HS_PRPR				(0xff << 8)
+#define HS_PREP				(0xff << 8)
 #define HS_ZERO				(0xff << 16)
 #define HS_TRAIL			(0xff << 24)
 
@@ -102,10 +102,16 @@
 #define CLK_TRAIL			(0xff << 24)
 
 #define DSI_PHY_TIMECON3	0x11c
-#define CLK_HS_PRPR			(0xff << 0)
+#define CLK_HS_PREP			(0xff << 0)
 #define CLK_HS_POST			(0xff << 8)
 #define CLK_HS_EXIT			(0xff << 16)
 
+#define T_LPX		5
+#define T_HS_PREP	6
+#define T_HS_TRAIL	8
+#define T_HS_EXIT	7
+#define T_HS_ZERO	10
+
 #define NS_TO_CYCLE(n, c)    ((n) / (c) + (((n) % (c)) ? 1 : 0))
 
 struct phy;
@@ -161,20 +167,18 @@ static void mtk_dsi_mask(struct mtk_dsi *dsi, u32 offset, u32 mask, u32 data)
 static void dsi_phy_timconfig(struct mtk_dsi *dsi)
 {
 	u32 timcon0, timcon1, timcon2, timcon3;
-	unsigned int ui, cycle_time;
-	unsigned int lpx;
+	u32 ui, cycle_time;
 
 	ui = 1000 / dsi->data_rate + 0x01;
 	cycle_time = 8000 / dsi->data_rate + 0x01;
-	lpx = 5;
 
-	timcon0 = (8 << 24) | (0xa << 16) | (0x6 << 8) | lpx;
-	timcon1 = (7 << 24) | (5 * lpx << 16) | ((3 * lpx) / 2) << 8 |
-		  (4 * lpx);
+	timcon0 = T_LPX | T_HS_PREP << 8 | T_HS_ZERO << 16 | T_HS_TRAIL << 24;
+	timcon1 = 4 * T_LPX | (3 * T_LPX / 2) << 8 | 5 * T_LPX << 16 |
+		  T_HS_EXIT << 24;
 	timcon2 = ((NS_TO_CYCLE(0x64, cycle_time) + 0xa) << 24) |
 		  (NS_TO_CYCLE(0x150, cycle_time) << 16);
-	timcon3 = (2 * lpx) << 16 | NS_TO_CYCLE(80 + 52 * ui, cycle_time) << 8 |
-		   NS_TO_CYCLE(0x40, cycle_time);
+	timcon3 = NS_TO_CYCLE(0x40, cycle_time) | (2 * T_LPX) << 16 |
+		  NS_TO_CYCLE(80 + 52 * ui, cycle_time) << 8;
 
 	writel(timcon0, dsi->regs + DSI_PHY_TIMECON0);
 	writel(timcon1, dsi->regs + DSI_PHY_TIMECON1);
@@ -202,19 +206,47 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
 {
 	struct device *dev = dsi->dev;
 	int ret;
+	u64 pixel_clock, total_bits;
+	u32 htotal, htotal_bits, bit_per_pixel, overhead_cycles, overhead_bits;
 
 	if (++dsi->refcount != 1)
 		return 0;
 
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB565:
+		bit_per_pixel = 16;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		bit_per_pixel = 18;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+	case MIPI_DSI_FMT_RGB888:
+	default:
+		bit_per_pixel = 24;
+		break;
+	}
+
 	/**
-	 * data_rate = (pixel_clock / 1000) * pixel_dipth * mipi_ratio;
-	 * pixel_clock unit is Khz, data_rata unit is MHz, so need divide 1000.
-	 * mipi_ratio is mipi clk coefficient for balance the pixel clk in mipi.
-	 * we set mipi_ratio is 1.05.
+	 * vm.pixelclock is in kHz, pixel_clock unit is Hz, so multiply by 1000
+	 * htotal_time = htotal * byte_per_pixel / num_lanes
+	 * overhead_time = lpx + hs_prepare + hs_zero + hs_trail + hs_exit
+	 * mipi_ratio = (htotal_time + overhead_time) / htotal_time
+	 * data_rate = pixel_clock * bit_per_pixel * mipi_ratio / num_lanes;
 	 */
-	dsi->data_rate = dsi->vm.pixelclock * 3 * 21 / (1 * 1000 * 10);
+	pixel_clock = dsi->vm.pixelclock * 1000;
+	htotal = dsi->vm.hactive + dsi->vm.hback_porch + dsi->vm.hfront_porch +
+			dsi->vm.hsync_len;
+	htotal_bits = htotal * bit_per_pixel;
+
+	overhead_cycles = T_LPX + T_HS_PREP + T_HS_ZERO + T_HS_TRAIL +
+			T_HS_EXIT;
+	overhead_bits = overhead_cycles * dsi->lanes * 8;
+	total_bits = htotal_bits + overhead_bits;
+
+	dsi->data_rate = DIV_ROUND_UP_ULL(pixel_clock * total_bits,
+					  htotal * dsi->lanes);
 
-	ret = clk_set_rate(dsi->hs_clk, dsi->data_rate * 1000000);
+	ret = clk_set_rate(dsi->hs_clk, dsi->data_rate);
 	if (ret < 0) {
 		dev_err(dev, "Failed to set data rate: %d\n", ret);
 		goto err_refcount;
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH V3 1/9] PM / OPP: Reword binding supporting multiple regulators per device
From: Viresh Kumar @ 2016-11-16  3:18 UTC (permalink / raw)
  To: Dave Gerlach
  Cc: Stephen Boyd, Rob Herring, Mark Brown, Rafael Wysocki, nm,
	Viresh Kumar, linaro-kernel, linux-pm, linux-kernel,
	Vincent Guittot, devicetree
In-Reply-To: <032e1bb4-c9e9-c588-a8c0-dd019bb64254@ti.com>

On 15-11-16, 16:11, Dave Gerlach wrote:
> On 11/15/2016 12:56 PM, Stephen Boyd wrote:
> >On 11/15, Viresh Kumar wrote:
> >>There are two important pieces of information we need for multiple
> >>regulator support:
> >>- Which regulator in the consumer node corresponds to which entry in
> >>  the OPP table. As Mark mentioned earlier, DT should be able to get
> >>  us this.
> >
> >This is also possible from C code though. Or is there some case
> >where it isn't possible if we're sharing the same table with two
> >devices? I'm lost on when this would ever happen.
> >
> >It feels like trying to keep the OPP table agnostic of the
> >consuming device and the device's binding is more trouble than
> >it's worth. Especially considering we have opp-shared and *-name
> >now.
> 
> I agree with this, I do not like having to pass a list of regulator names to
> the opp core that I *hope* the device I am controlling has provided.

What do you mean by that? Are you saying this from DT's point of view
or of the code? i.e. Are you saying that you don't like the
dev_pm_opp_set_regulators() API ?

> The
> intent seems to be to use the cpufreq-dt driver as is and not pass any

I would like to kill all regulators code from cpufreq-dt sometime
soon. All that is left there is making sure we have a regulator in
place, but I strongly feel OPP core is the right place for doing that
now.

> cpu-supply anymore so the cpufreq-dt driver has no knowledge of what
> regulators are present (it operates as it would today on a system with no
> regulator required). But as is it will move forward regardless of whether or
> not we actually intended to provide a multi regulator set up or platform
> set_opp helper, and this probably isn't ideal.

Yes and that's why I am more inclined towards my above comment. We
shall make it consistent.

> I would think cpufreq-dt/opp
> core should be have knowledge of what regulators are needed to achieve these
> opp transitions and make sure everything is in place before moving ahead.

The last patch in my series does what you are looking for:

PM / OPP: Don't assume platform doesn't have regulators

Isn't it ?

-- 
viresh

^ permalink raw reply

* Re: [PATCH V3 1/9] PM / OPP: Reword binding supporting multiple regulators per device
From: Viresh Kumar @ 2016-11-16  3:08 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Rob Herring, Mark Brown, Rafael Wysocki, nm-l0cyMroinI0,
	Viresh Kumar, linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Vincent Guittot,
	d-gerlach-l0cyMroinI0, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161115185645.GA25626-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

On 15-11-16, 10:56, Stephen Boyd wrote:
> This is also possible from C code though.

Right and this is what this patchset is doing right now. To make it
clear, the order of regulator names in the call
dev_pm_opp_set_regulators() is used now to communicate the order in
which entries are present in the OPP table.

> Or is there some case
> where it isn't possible if we're sharing the same table with two
> devices?

Even in that case it will be possible to set regulators separately, so
that's not a problem.

> I'm lost on when this would ever happen.

It would happen in case of Krait for example, where CPUs manage DVFS
separately but their tables may all be same.

> It feels like trying to keep the OPP table agnostic of the
> consuming device and the device's binding is more trouble than
> it's worth. Especially considering we have opp-shared and *-name
> now.

Right.

> > - The order in which the supplies need to be programmed. We have all
> >   agreed to do this in code instead of inferring it from DT and this
> >   patch series already does that.
> 
> Agreed. Encoding a sequence into DT doesn't sound very feasible.
> How is this going to be handled though? I don't see any users of
> the code we're reviewing here, so it's hard to grasp how things
> will work. It would be really useful if we had some user of the
> code included in the patch series to get the big picture.

The TI guys would be doing it soon. The sequence will be handled by
platform specific set_opp() callbacks now. So, there is nothing in the
core for that.

> > So, are you saying that the way this patchset does it is fine with you
> > ?
> 
> That's just to handle the ordering of operations?

Not just that. The blocking question here is that "Do we want to know
the sequence in which the entries for multiple regulators are present
in the OPP nodes from the DT? Or is it fine to handle that in code".

And AFAIU, you are saying that we better handle that in code as
handling that in DT is going to be nightmare without a new ugly
property.

-- 
viresh
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v4 12/12] ASoC: add simple-graph-scu-card support
From: Kuninori Morimoto @ 2016-11-16  2:22 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx@renesas.com>


From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

graph base DT binding are used on V4L2, and ALSA SoC is using different
style of DT. In case of simple case, ALSA SoC supports simple-card
driver.
In the future, V4L2 / ALSA will support HDMI, and then, DT bindings
between V4L2 / ALSA should be merged somehow.
Sometimes, we would like to use DPCM base simple-card on graph base DT.
This patch adds graph base DT binding of simple-scu-card

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
v3 -> v4

 - no change

 sound/soc/generic/Kconfig                 |   8 +
 sound/soc/generic/Makefile                |   2 +
 sound/soc/generic/simple-graph-scu-card.c | 417 ++++++++++++++++++++++++++++++
 3 files changed, 427 insertions(+)
 create mode 100644 sound/soc/generic/simple-graph-scu-card.c

diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index efefabd..fc11828 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -21,3 +21,11 @@ config SND_SIMPLE_GRAPH_CARD
 	select SND_SIMPLE_CARD_UTILS
 	help
 	  This option enables generic simple Graph sound card support
+
+config SND_SIMPLE_GRAPH_SCU_CARD
+	tristate "ASoC Simple Graph SCU sound card support"
+	depends on OF
+	select SND_SIMPLE_CARD_UTILS
+	help
+	  This option enables generic simple Graph SCU sound card support.
+	  It supports DPCM of multi CPU single Codec ststem.
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index 94eb6f1..fd75b55 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -2,8 +2,10 @@ snd-soc-simple-card-utils-objs	:= simple-card-utils.o
 snd-soc-simple-card-objs	:= simple-card.o
 snd-soc-simple-scu-card-objs	:= simple-scu-card.o
 snd-soc-simple-graph-card-objs	:= simple-graph-card.o
+snd-soc-simple-graph-scu-card-objs	:= simple-graph-scu-card.o
 
 obj-$(CONFIG_SND_SIMPLE_CARD_UTILS)	+= snd-soc-simple-card-utils.o
 obj-$(CONFIG_SND_SIMPLE_CARD)		+= snd-soc-simple-card.o
 obj-$(CONFIG_SND_SIMPLE_SCU_CARD)	+= snd-soc-simple-scu-card.o
 obj-$(CONFIG_SND_SIMPLE_GRAPH_CARD)	+= snd-soc-simple-graph-card.o
+obj-$(CONFIG_SND_SIMPLE_GRAPH_SCU_CARD)	+= snd-soc-simple-graph-scu-card.o
diff --git a/sound/soc/generic/simple-graph-scu-card.c b/sound/soc/generic/simple-graph-scu-card.c
new file mode 100644
index 0000000..39a5019
--- /dev/null
+++ b/sound/soc/generic/simple-graph-scu-card.c
@@ -0,0 +1,417 @@
+/*
+ * ASoC simple graph SCU sound card support
+ *
+ * Copyright (C) 2016 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on
+ *	${LINUX}/sound/soc/generic/simple-graph-card.c
+ *	${LINUX}/sound/soc/generic/simple-scu-card.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/jack.h>
+#include <sound/simple_card_utils.h>
+
+struct simple_card_data {
+	struct snd_soc_card snd_card;
+	struct snd_soc_codec_conf codec_conf;
+	struct asoc_simple_dai *dai_props;
+	struct snd_soc_dai_link *dai_link;
+	u32 convert_rate;
+	u32 convert_channels;
+};
+
+#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
+#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
+#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+
+#define PREFIX	"simple-audio-card,"
+
+static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct asoc_simple_dai *dai_props =
+		simple_priv_to_props(priv, rtd->num);
+
+	return clk_prepare_enable(dai_props->clk);
+}
+
+static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct asoc_simple_dai *dai_props =
+		simple_priv_to_props(priv, rtd->num);
+
+	clk_disable_unprepare(dai_props->clk);
+}
+
+static struct snd_soc_ops asoc_simple_card_ops = {
+	.startup = asoc_simple_card_startup,
+	.shutdown = asoc_simple_card_shutdown,
+};
+
+static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct snd_soc_dai *dai;
+	struct snd_soc_dai_link *dai_link;
+	struct asoc_simple_dai *dai_props;
+	int num = rtd->num;
+
+	dai_link	= simple_priv_to_link(priv, num);
+	dai_props	= simple_priv_to_props(priv, num);
+	dai		= dai_link->dynamic ?
+				rtd->cpu_dai :
+				rtd->codec_dai;
+
+	return asoc_simple_card_init_dai(dai, dai_props);
+}
+
+static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					       struct snd_pcm_hw_params *params)
+{
+	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+	struct snd_interval *rate = hw_param_interval(params,
+						      SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+							  SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	if (priv->convert_rate)
+		rate->min =
+		rate->max = priv->convert_rate;
+
+	if (priv->convert_channels)
+		channels->min =
+		channels->max = priv->convert_channels;
+
+	return 0;
+}
+
+static int asoc_simple_card_dai_link_of(struct device_node *port,
+					struct device_node *ep,
+					struct simple_card_data *priv,
+					unsigned int daifmt,
+					int idx, int is_fe)
+{
+	struct device *dev = simple_priv_to_dev(priv);
+	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
+	struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
+	int ret;
+
+	if (is_fe) {
+		/* BE is dummy */
+		dai_link->codec_of_node		= NULL;
+		dai_link->codec_dai_name	= "snd-soc-dummy-dai";
+		dai_link->codec_name		= "snd-soc-dummy";
+
+		/* FE settings */
+		dai_link->dynamic		= 1;
+		dai_link->dpcm_merged_format	= 1;
+
+		ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
+		if (ret)
+			return ret;
+
+		ret = asoc_simple_card_parse_clk_cpu(ep, dai_link, dai_props);
+		if (ret < 0)
+			return ret;
+
+		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+							"fe.%s",
+							dai_link->cpu_dai_name);
+		if (ret < 0)
+			return ret;
+
+		/* snd_card.num_links includes Codec */
+		asoc_simple_card_canonicalize_cpu(dai_link,
+					(priv->snd_card.num_links -1) == 1);
+	} else {
+		/* FE is dummy */
+		dai_link->cpu_of_node		= NULL;
+		dai_link->cpu_dai_name		= "snd-soc-dummy-dai";
+		dai_link->cpu_name		= "snd-soc-dummy";
+
+		/* BE settings */
+		dai_link->no_pcm		= 1;
+		dai_link->be_hw_params_fixup	= asoc_simple_card_be_hw_params_fixup;
+
+		ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
+		if (ret < 0)
+			return ret;
+
+		ret = asoc_simple_card_parse_clk_codec(ep, dai_link, dai_props);
+		if (ret < 0)
+			return ret;
+
+		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+							"be.%s",
+							dai_link->codec_dai_name);
+		if (ret < 0)
+			return ret;
+
+		snd_soc_of_parse_audio_prefix_from_node(&priv->snd_card,
+							port,
+							&priv->codec_conf,
+							dai_link->codec_of_node,
+							PREFIX "prefix");
+	}
+
+	ret = snd_soc_of_parse_tdm_slot(ep,
+					&dai_props->tx_slot_mask,
+					&dai_props->rx_slot_mask,
+					&dai_props->slots,
+					&dai_props->slot_width);
+	if (ret)
+		return ret;
+
+	ret = asoc_simple_card_canonicalize_dailink(dai_link);
+	if (ret < 0)
+		return ret;
+
+	dai_link->dai_fmt		= daifmt;
+	dai_link->dpcm_playback		= 1;
+	dai_link->dpcm_capture		= 1;
+	dai_link->ops			= &asoc_simple_card_ops;
+	dai_link->init			= asoc_simple_card_dai_init;
+
+	dev_dbg(dev, "\t%s / %04x / %d\n",
+		dai_link->name,
+		dai_link->dai_fmt,
+		dai_props->sysclk);
+
+	return 0;
+}
+
+static int asoc_simple_card_parse_of(struct device_node *node,
+				     struct simple_card_data *priv)
+{
+	struct device *dev = simple_priv_to_dev(priv);
+	struct device *cpu_dev = dev->parent;
+	struct device_node *ports = of_graph_get_top_port(cpu_dev);
+	struct snd_soc_card *card = &priv->snd_card;
+	struct device_node *port, *cpu_ep, *r_cpu_ep, *codec_ep;
+	unsigned int daifmt = 0;
+	int i, ret, done;
+
+	if (!node)
+		return -EINVAL;
+
+	ret = snd_soc_of_parse_audio_routing_from_node(&priv->snd_card,
+						       ports, PREFIX "routing");
+	if (ret)
+		return ret;
+
+	/* sampling rate convert */
+	of_property_read_u32(ports, PREFIX "convert-rate",
+			     &priv->convert_rate);
+
+	/* channels transfer */
+	of_property_read_u32(ports, PREFIX "convert-channels",
+			     &priv->convert_channels);
+
+	/*
+	 * it supports multi CPU, single CODEC only here.
+	 */
+
+	/* find 1st codec */
+	done = 0;
+	for_each_of_port(node, port) {
+		/* keep for_each for of_node_get/of_node_put */
+		if (done)
+			continue;
+
+		for_each_of_endpoint_in_port(port, cpu_ep) {
+			/* keep for_each for of_node_get/of_node_put */
+			if (done)
+				continue;
+
+			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
+			r_cpu_ep = of_graph_get_remote_endpoint(codec_ep);
+			of_node_put(codec_ep);
+			of_node_put(r_cpu_ep);
+			if (cpu_ep != r_cpu_ep) {
+				ret = -EINVAL;
+				goto parse_of_err;
+			}
+
+			ret = asoc_simple_card_parse_daifmt(dev,
+							    ports, codec_ep,
+							    PREFIX, &daifmt);
+			if (ret < 0)
+				goto parse_of_err;
+
+			done = 1;
+		}
+	}
+
+	/* Front-End (= CPU) */
+	i = 0;
+	for_each_of_port(node, port) {
+		for_each_of_endpoint_in_port(port, cpu_ep) {
+			ret = asoc_simple_card_dai_link_of(
+				port, cpu_ep, priv, daifmt, i, 1);
+			if (ret < 0)
+				goto parse_of_err;
+			i++;
+		}
+	}
+
+	/* Back-End (= Codec) */
+	done = 0;
+	for_each_of_port(node, port) {
+		/* keep for_each for of_node_get/of_node_put */
+		if (done)
+			continue;
+
+		for_each_of_endpoint_in_port(port, cpu_ep) {
+			/* keep for_each for of_node_get/of_node_put */
+			if (done)
+				continue;
+
+			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
+			r_cpu_ep = of_graph_get_remote_endpoint(codec_ep);
+			of_node_put(codec_ep);
+			of_node_put(r_cpu_ep);
+			if (cpu_ep != r_cpu_ep) {
+				ret = -EINVAL;
+				goto parse_of_err;
+			}
+
+			ret = asoc_simple_card_dai_link_of(
+				port, codec_ep, priv, daifmt, i, 0);
+			if (ret < 0)
+				goto parse_of_err;
+			i++;
+
+			done = 1;
+		}
+	}
+
+	ret = asoc_simple_card_parse_card_name(card, ports, PREFIX);
+	if (ret)
+		return ret;
+
+	dev_dbg(dev, "New card: %s\n",
+		priv->snd_card.name ? priv->snd_card.name : "");
+	dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
+	dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
+
+	return 0;
+parse_of_err:
+	of_node_put(port);
+	of_node_put(cpu_ep);
+
+	return ret;
+}
+
+static int asoc_simple_card_probe(struct platform_device *pdev)
+{
+	struct simple_card_data *priv;
+	struct snd_soc_dai_link *dai_link;
+	struct asoc_simple_dai *dai_props;
+	struct device *dev = &pdev->dev;
+	struct device *cpu_dev = pdev->dev.parent;
+	struct device_node *cpu_node = cpu_dev->of_node;
+	struct asoc_simple_graph_card_info *info = pdev->dev.platform_data;
+	int num, ret;
+
+	/* Allocate the private data and the DAI link array */
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* it supports multi CPU, single CODEC only here */
+	num = info->endpoint_num + 1; /* +1 for Codec */
+
+	dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
+	dai_link  = devm_kzalloc(dev, sizeof(*dai_link)  * num, GFP_KERNEL);
+	if (!dai_props || !dai_link)
+		return -ENOMEM;
+
+	priv->dai_props			= dai_props;
+	priv->dai_link			= dai_link;
+
+	/* Init snd_soc_card */
+	priv->snd_card.owner		= THIS_MODULE;
+	priv->snd_card.dev		= dev;
+	priv->snd_card.dai_link		= priv->dai_link;
+	priv->snd_card.num_links	= num;
+	priv->snd_card.codec_conf	= &priv->codec_conf;
+	priv->snd_card.num_configs	= 1;
+
+	ret = asoc_simple_card_parse_of(cpu_node, priv);
+	if (ret < 0) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "parse error %d\n", ret);
+		goto err;
+	}
+
+	/*
+	 * FIXME
+	 *
+	 * This driver is assuming that it will be called from
+	 * asoc_simple_card_try_to_probe_graph_card() which
+	 * is using platform_device_register_full().
+	 * This means it is not came from DT. But this driver itself
+	 * will be used as part of ALSA SoC (= sound card).
+	 * Because of these background, it might fail in
+	 * snd_pcm_lib_malloc_pages() on .hw_params.
+	 * Because, noone cares its dma_ops, and result of get_dma_ops()
+	 * is based on its architecture.
+	 * So, it should call arch_setup_dma_ops() from somewhere,
+	 * otherwise, for example, ARM is no problem, but ARM64 will be fail.
+	 * But, of_platform_device_xxx() are not good solution today.
+	 * This driver calls it by itself here. Please fixme
+	 * see also
+	 * linux/sound/soc/generic/simple-card-utils.c ::
+	 *	asoc_simple_card_try_to_probe_graph_card()
+	 */
+	of_dma_configure(dev, dev->of_node);
+
+	snd_soc_card_set_drvdata(&priv->snd_card, priv);
+
+	ret = devm_snd_soc_register_card(dev, &priv->snd_card);
+	if (ret >= 0)
+		return ret;
+err:
+	asoc_simple_card_clean_reference(&priv->snd_card);
+
+	return ret;
+}
+
+static int asoc_simple_card_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
+
+	return asoc_simple_card_clean_reference(&priv->snd_card);
+}
+
+static struct platform_driver asoc_simple_card = {
+	.driver = {
+		.name = "asoc-simple-graph-scu-card",
+	},
+	.probe = asoc_simple_card_probe,
+	.remove = asoc_simple_card_remove,
+};
+module_platform_driver(asoc_simple_card);
+
+MODULE_ALIAS("platform:asoc-simple-graph-scu-card");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Simple Graph SCU Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 11/12] ASoC: add simple-graph-scu-card document
From: Kuninori Morimoto @ 2016-11-16  2:22 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>


From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 .../bindings/sound/simple-graph-scu-card.txt       | 65 ++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/simple-graph-scu-card.txt

diff --git a/Documentation/devicetree/bindings/sound/simple-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-graph-scu-card.txt
new file mode 100644
index 0000000..223096a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/simple-graph-scu-card.txt
@@ -0,0 +1,65 @@
+Simple-Graph-SCU-Card:
+
+Simple-Graph-SCU-Card specifies audio DAI connections of SoC <-> codec.
+It is based on common bindings for device graphs.
+see ${LINUX}/Documentation/devicetree/bindings/graph.txt
+
+Basically, Simple-Graph-SCU-Card property is same as Simple-Card / Simple-Graph-Card.
+see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
+    ${LINUX}/Documentation/devicetree/bindings/sound/simple-graph-card.txt
+
+Main difference between Simple-Graph-Card and Simple-Graph-SCU-Card is that
+Simple-Graph-SCU-Card can use multi CPU.
+
+Required properties:
+
+- compatible				: "asoc-simple-graph-scu-card";
+- simple-audio-card,routing		: see simple-card.txt
+
+Example
+
+ak4643: codec@12 {
+	compatible = "asahi-kasei,ak4643";
+	...
+	port {
+		ak4643_fe: endpoint@0 {
+			remote-endpoint = <&rsnd_fe>;
+			...
+		};
+		ak4643_be: endpoint@1 {
+			remote-endpoint = <&rsnd_be>;
+			...
+		};
+	};
+};
+
+rcar_sound {
+	...
+	ports {
+		compatible = "asoc-simple-graph-scu-card";
+
+		simple-audio-card,name = "graph-sound";
+		simple-audio-card,format = "left_j";
+		simple-audio-card,bitclock-master = <&rsnd_fe>;
+		simple-audio-card,frame-master = <&rsnd_fe>;
+		simple-audio-card,convert-rate = <48000>;
+		simple-audio-card,convert-channels = <2>;
+		simple-audio-card,prefix = "ak4642";
+		simple-audio-card,routing =
+				"ak4642 Playback", "DAI0 Playback",
+				"ak4642 Playback", "DAI1 Playback";
+
+		port@0 {
+			rsnd_fe: endpoint@0 {
+				remote-endpoint = <&ak4643_fe>;
+				...
+			};
+		};
+		port@1 {
+			rsnd_be: endpoint@1 {
+				remote-endpoint = <&ak4643_be>;
+				...
+			};
+		};
+	};
+};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 10/12] ASoC: add simple-graph-card support
From: Kuninori Morimoto @ 2016-11-16  2:22 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx@renesas.com>


From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

graph base DT binding are used on V4L2, and ALSA SoC is using different
style of DT. In case of simple case, ALSA SoC supports simple-card
driver.
In the future, V4L2 / ALSA will support HDMI, and then, DT bindings
between V4L2 / ALSA should be merged somehow.
This patch adds graph base DT binding with simple-card style

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
v3 -> v4

 - no change

 sound/soc/generic/Kconfig             |   7 +
 sound/soc/generic/Makefile            |   2 +
 sound/soc/generic/simple-graph-card.c | 462 ++++++++++++++++++++++++++++++++++
 3 files changed, 471 insertions(+)
 create mode 100644 sound/soc/generic/simple-graph-card.c

diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index d023959..efefabd 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -14,3 +14,10 @@ config SND_SIMPLE_SCU_CARD
 	help
 	  This option enables generic simple SCU sound card support.
 	  It supports DPCM of multi CPU single Codec system.
+
+config SND_SIMPLE_GRAPH_CARD
+	tristate "ASoC Simple Graph sound card support"
+	depends on OF
+	select SND_SIMPLE_CARD_UTILS
+	help
+	  This option enables generic simple Graph sound card support
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index ee750f3..94eb6f1 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -1,7 +1,9 @@
 snd-soc-simple-card-utils-objs	:= simple-card-utils.o
 snd-soc-simple-card-objs	:= simple-card.o
 snd-soc-simple-scu-card-objs	:= simple-scu-card.o
+snd-soc-simple-graph-card-objs	:= simple-graph-card.o
 
 obj-$(CONFIG_SND_SIMPLE_CARD_UTILS)	+= snd-soc-simple-card-utils.o
 obj-$(CONFIG_SND_SIMPLE_CARD)		+= snd-soc-simple-card.o
 obj-$(CONFIG_SND_SIMPLE_SCU_CARD)	+= snd-soc-simple-scu-card.o
+obj-$(CONFIG_SND_SIMPLE_GRAPH_CARD)	+= snd-soc-simple-graph-card.o
diff --git a/sound/soc/generic/simple-graph-card.c b/sound/soc/generic/simple-graph-card.c
new file mode 100644
index 0000000..a98b13f
--- /dev/null
+++ b/sound/soc/generic/simple-graph-card.c
@@ -0,0 +1,462 @@
+/*
+ * ASoC simple graph sound card support
+ *
+ * Copyright (C) 2016 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on ${LINUX}/sound/soc/generic/simple-card.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/jack.h>
+#include <sound/simple_card_utils.h>
+
+struct asoc_simple_jack {
+	struct snd_soc_jack jack;
+	struct snd_soc_jack_pin pin;
+	struct snd_soc_jack_gpio gpio;
+};
+
+struct simple_card_data {
+	struct snd_soc_card snd_card;
+	struct simple_dai_props {
+		struct asoc_simple_dai cpu_dai;
+		struct asoc_simple_dai codec_dai;
+		unsigned int mclk_fs;
+	} *dai_props;
+	struct asoc_simple_jack hp_jack;
+	struct asoc_simple_jack mic_jack;
+	struct snd_soc_dai_link *dai_link;
+	unsigned int mclk_fs;
+};
+
+#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
+#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
+#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+
+#define PREFIX	"simple-audio-card,"
+
+#define asoc_simple_card_init_hp(card, node, sjack, prefix)	\
+	asoc_simple_card_init_jack(card, node, sjack, 1, prefix)
+#define asoc_simple_card_init_mic(card, node, sjack, prefix)	\
+	asoc_simple_card_init_jack(card, node, sjack, 0, prefix)
+static int asoc_simple_card_init_jack(struct snd_soc_card *card,
+				      struct device_node *node,
+				      struct asoc_simple_jack *sjack,
+				      int is_hp, char *prefix)
+{
+	enum of_gpio_flags flags;
+	char prop[128];
+	char *pin_name;
+	char *gpio_name;
+	int mask;
+	int det;
+
+	sjack->gpio.gpio = -ENOENT;
+
+	if (is_hp) {
+		snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
+		pin_name	= "Headphones";
+		gpio_name	= "Headphone detection";
+		mask		= SND_JACK_HEADPHONE;
+	} else {
+		snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
+		pin_name	= "Mic Jack";
+		gpio_name	= "Mic detection";
+		mask		= SND_JACK_MICROPHONE;
+	}
+
+	det = of_get_named_gpio_flags(node, prop, 0, &flags);
+	if (det == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	if (gpio_is_valid(det)) {
+		sjack->pin.pin		= pin_name;
+		sjack->pin.mask		= mask;
+
+		sjack->gpio.name	= gpio_name;
+		sjack->gpio.report	= mask;
+		sjack->gpio.gpio	= det;
+		sjack->gpio.invert	= !!(flags & OF_GPIO_ACTIVE_LOW);
+		sjack->gpio.debounce_time = 150;
+
+		snd_soc_card_jack_new(card, pin_name, mask,
+				      &sjack->jack,
+				      &sjack->pin, 1);
+
+		snd_soc_jack_add_gpios(&sjack->jack, 1,
+				       &sjack->gpio);
+	}
+
+	return 0;
+}
+
+static void asoc_simple_card_remove_jack(struct asoc_simple_jack *sjack)
+{
+	if (gpio_is_valid(sjack->gpio.gpio))
+		snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio);
+}
+
+static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct simple_dai_props *dai_props =
+		simple_priv_to_props(priv, rtd->num);
+	int ret;
+
+	ret = clk_prepare_enable(dai_props->cpu_dai.clk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(dai_props->codec_dai.clk);
+	if (ret)
+		clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+	return ret;
+}
+
+static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct simple_dai_props *dai_props =
+		simple_priv_to_props(priv, rtd->num);
+
+	clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+	clk_disable_unprepare(dai_props->codec_dai.clk);
+}
+
+static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+	struct simple_dai_props *dai_props =
+		simple_priv_to_props(priv, rtd->num);
+	unsigned int mclk, mclk_fs = 0;
+	int ret = 0;
+
+	if (priv->mclk_fs)
+		mclk_fs = priv->mclk_fs;
+	else if (dai_props->mclk_fs)
+		mclk_fs = dai_props->mclk_fs;
+
+	if (mclk_fs) {
+		mclk = params_rate(params) * mclk_fs;
+		ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+					     SND_SOC_CLOCK_IN);
+		if (ret && ret != -ENOTSUPP)
+			goto err;
+
+		ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+					     SND_SOC_CLOCK_OUT);
+		if (ret && ret != -ENOTSUPP)
+			goto err;
+	}
+	return 0;
+err:
+	return ret;
+}
+
+static struct snd_soc_ops asoc_simple_card_ops = {
+	.startup = asoc_simple_card_startup,
+	.shutdown = asoc_simple_card_shutdown,
+	.hw_params = asoc_simple_card_hw_params,
+};
+
+static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct device *dev = simple_priv_to_dev(priv);
+	struct device *cpu_dev = dev->parent;
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	struct snd_soc_card *card = rtd->card;
+	struct simple_dai_props *dai_props =
+		simple_priv_to_props(priv, rtd->num);
+	struct device_node *cpu_port = of_graph_get_top_port(cpu_dev);
+	int ret;
+
+	ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
+	if (ret < 0)
+		return ret;
+
+	ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
+	if (ret < 0)
+		return ret;
+
+	ret = asoc_simple_card_init_hp(card, cpu_port, &priv->hp_jack, PREFIX);
+	if (ret < 0)
+		return ret;
+
+	ret = asoc_simple_card_init_mic(card, cpu_port, &priv->hp_jack, PREFIX);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int asoc_simple_card_dai_link_of(struct device_node *cpu_ep,
+					struct simple_card_data *priv,
+					int idx)
+{
+	struct device *dev = simple_priv_to_dev(priv);
+	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
+	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
+	struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
+	struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
+	struct device_node *cpu_port;
+	struct device_node *cpu_remote_ep;
+	struct device_node *codec_ep;
+	int ret;
+
+	codec_ep	= of_graph_get_remote_endpoint(cpu_ep);
+	cpu_remote_ep	= of_graph_get_remote_endpoint(codec_ep);
+
+	if (cpu_ep != cpu_remote_ep) {
+		dev_err(dev, "endpoint parse error\n");
+		ret = -EINVAL;
+		goto dai_link_of_err;
+	}
+
+	cpu_port	= cpu_ep->parent;
+
+	ret = asoc_simple_card_parse_daifmt(dev, cpu_port, codec_ep,
+					    PREFIX, &dai_link->dai_fmt);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	of_property_read_u32(cpu_port, "mclk-fs", &dai_props->mclk_fs);
+
+	ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = snd_soc_of_parse_tdm_slot(cpu_ep,
+					&cpu_dai->tx_slot_mask,
+					&cpu_dai->rx_slot_mask,
+					&cpu_dai->slots,
+					&cpu_dai->slot_width);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = snd_soc_of_parse_tdm_slot(codec_ep,
+					&codec_dai->tx_slot_mask,
+					&codec_dai->rx_slot_mask,
+					&codec_dai->slots,
+					&codec_dai->slot_width);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_parse_clk_cpu(cpu_ep, dai_link, cpu_dai);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_parse_clk_codec(codec_ep, dai_link, codec_dai);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_canonicalize_dailink(dai_link);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+						"%s-%s",
+						dai_link->cpu_dai_name,
+						dai_link->codec_dai_name);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	dai_link->ops = &asoc_simple_card_ops;
+	dai_link->init = asoc_simple_card_dai_init;
+
+	dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
+	dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
+	dev_dbg(dev, "\tcpu : %s / %d\n",
+		dai_link->cpu_dai_name,
+		dai_props->cpu_dai.sysclk);
+	dev_dbg(dev, "\tcodec : %s / %d\n",
+		dai_link->codec_dai_name,
+		dai_props->codec_dai.sysclk);
+
+	asoc_simple_card_canonicalize_cpu(dai_link,
+					  priv->snd_card.num_links == 1);
+
+dai_link_of_err:
+	of_node_put(codec_ep);
+	of_node_put(cpu_remote_ep);
+
+	return ret;
+}
+
+static int asoc_simple_card_parse_of(struct device_node *node,
+				     struct simple_card_data *priv)
+{
+	struct device *dev = simple_priv_to_dev(priv);
+	struct device *cpu_dev = dev->parent;
+	struct device_node *top_port = of_graph_get_top_port(cpu_dev);
+	struct snd_soc_card *card = &priv->snd_card;
+	struct device_node *port, *ep;
+	int i = 0, ret;
+
+	if (!node)
+		return -EINVAL;
+
+	for_each_of_port(node, port) {
+		/* The off-codec widgets */
+		if (of_property_read_bool(port, PREFIX "widgets")) {
+			ret = snd_soc_of_parse_audio_simple_widgets_from_node(
+				&priv->snd_card,
+				port, PREFIX "widgets");
+			if (ret)
+				return ret;
+		}
+
+		/* DAPM routes */
+		if (of_property_read_bool(port, PREFIX "routing")) {
+			ret = snd_soc_of_parse_audio_routing_from_node(
+				&priv->snd_card,
+				port, PREFIX "routing");
+			if (ret)
+				return ret;
+		}
+
+		/* Factor to mclk, used in hw_params() */
+		of_property_read_u32(port, PREFIX "mclk-fs", &priv->mclk_fs);
+
+		for_each_of_endpoint_in_port(port, ep) {
+			ret = asoc_simple_card_dai_link_of(ep, priv, i);
+			if (ret < 0) {
+				of_node_put(ep);
+				return ret;
+			}
+			i++;
+		}
+	}
+
+	ret = asoc_simple_card_parse_card_name(card, top_port, PREFIX);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int asoc_simple_card_probe(struct platform_device *pdev)
+{
+	struct simple_card_data *priv;
+	struct snd_soc_dai_link *dai_link;
+	struct simple_dai_props *dai_props;
+	struct device *dev = &pdev->dev;
+	struct device *cpu_dev = pdev->dev.parent;
+	struct device_node *cpu_node = cpu_dev->of_node;
+	struct asoc_simple_graph_card_info *info = pdev->dev.platform_data;
+	int num, ret;
+
+	if (!info)
+		return -EINVAL;
+
+	/* Allocate the private data and the DAI link array */
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	num = info->endpoint_num;
+
+	dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
+	dai_link  = devm_kzalloc(dev, sizeof(*dai_link)  * num, GFP_KERNEL);
+	if (!dai_props || !dai_link)
+		return -ENOMEM;
+
+	priv->dai_props			= dai_props;
+	priv->dai_link			= dai_link;
+
+	/* Init snd_soc_card */
+	priv->snd_card.owner		= THIS_MODULE;
+	priv->snd_card.dev		= dev;
+	priv->snd_card.dai_link		= priv->dai_link;
+	priv->snd_card.num_links	= num;
+
+	ret = asoc_simple_card_parse_of(cpu_node, priv);
+	if (ret < 0) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "parse error %d\n", ret);
+		goto err;
+	}
+
+	/*
+	 * FIXME
+	 *
+	 * This driver is assuming that it will be called from
+	 * asoc_simple_card_try_to_probe_graph_card() which
+	 * is using platform_device_register_full().
+	 * This means it is not came from DT. But this driver itself
+	 * will be used as part of ALSA SoC (= sound card).
+	 * Because of these background, it might fail in
+	 * snd_pcm_lib_malloc_pages() on .hw_params.
+	 * Because, noone cares its dma_ops, and result of get_dma_ops()
+	 * is based on its architecture.
+	 * So, it should call arch_setup_dma_ops() from somewhere,
+	 * otherwise, for example, ARM is no problem, but ARM64 will be fail.
+	 * But, of_platform_device_xxx() are not good solution today.
+	 * This driver calls it by itself here. Please fixme
+	 * see also
+	 * linux/sound/soc/generic/simple-card-utils.c ::
+	 *	asoc_simple_card_try_to_probe_graph_card()
+	 */
+	of_dma_configure(dev, dev->of_node);
+
+	snd_soc_card_set_drvdata(&priv->snd_card, priv);
+
+	ret = devm_snd_soc_register_card(dev, &priv->snd_card);
+	if (ret >= 0)
+		return ret;
+err:
+	asoc_simple_card_clean_reference(&priv->snd_card);
+
+	return ret;
+}
+
+static int asoc_simple_card_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
+
+	asoc_simple_card_remove_jack(&priv->hp_jack);
+	asoc_simple_card_remove_jack(&priv->mic_jack);
+
+	return asoc_simple_card_clean_reference(&priv->snd_card);
+}
+
+static struct platform_driver asoc_simple_card = {
+	.driver = {
+		.name = "asoc-simple-graph-card",
+	},
+	.probe = asoc_simple_card_probe,
+	.remove = asoc_simple_card_remove,
+};
+module_platform_driver(asoc_simple_card);
+
+MODULE_ALIAS("platform:asoc-simple-graph-card");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Simple Graph Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 09/12] ASoC: add simple-graph-card document
From: Kuninori Morimoto @ 2016-11-16  2:21 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>


From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 .../bindings/sound/simple-graph-card.txt           | 65 ++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/simple-graph-card.txt

diff --git a/Documentation/devicetree/bindings/sound/simple-graph-card.txt b/Documentation/devicetree/bindings/sound/simple-graph-card.txt
new file mode 100644
index 0000000..11823b0
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/simple-graph-card.txt
@@ -0,0 +1,65 @@
+Simple-Graph-Card:
+
+Simple-Graph-Card specifies audio DAI connections of SoC <-> codec.
+It is based on common bindings for device graphs.
+see ${LINUX}/Documentation/devicetree/bindings/graph.txt
+
+Basically, Simple-Graph-Card property is same as Simple-Card.
+see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
+
+Below are same as Simple-Card.
+
+- simple-audio-card,name
+- simple-audio-card,widgets
+- simple-audio-card,routing
+- simple-audio-card,mclk-fs
+- simple-audio-card,hp-det-gpio
+- simple-audio-card,mic-det-gpio
+- simple-audio-card,format
+- simple-audio-card,frame-master
+- simple-audio-card,bitclock-master
+- simple-audio-card,bitclock-inversion
+- simple-audio-card,frame-inversion
+- simple-audio-card,mclk-fs
+- simple-audio-card,dai-tdm-slot-num
+- simple-audio-card,dai-tdm-slot-width
+- clocks / system-clock-frequency
+
+In Simple-Graph-Card, above properties need in CPU side port on DT.
+And it needs to have "compatible" property too.
+In addition, CPU side driver needs to call asoc_simple_card_try_to_probe_graph_card().
+It will probe specified Card driver if it could find "compatible" property on port.
+Otherwise, it will do nothing.
+
+Required properties:
+
+- compatible				: "asoc-simple-graph-card";
+
+Example
+
+ak4643: codec@12 {
+	compatible = "asahi-kasei,ak4643";
+	...
+	port {
+		ak4643_port: endpoint {
+			remote-endpoint = <&rcar_ak4643_port>;
+			clocks = <&audio_clock>;
+		};
+	};
+};
+
+rcar_sound {
+	...
+	port {
+		compatible = "asoc-simple-graph-card";
+
+		simple-audio-card,format = "left_j";
+		simple-audio-card,bitclock-master = <&ak4643_port>;
+		simple-audio-card,frame-master = <&ak4643_port>;
+
+		rcar_ak4643_port: endpoint {
+			remote-endpoint = <&ak4643_port>;
+			...
+		};
+	};
+};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 08/12] ASoC: simple-card-utils: adjust for graph on asoc_simple_card_parse_card_name
From: Kuninori Morimoto @ 2016-11-16  2:21 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

It is assuming that the card related information is located on
"card" node, but graph case doesn't have it.
This patch adds node parameter to adjust for graph support

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 include/sound/simple_card_utils.h     | 1 +
 sound/soc/generic/simple-card-utils.c | 3 ++-
 sound/soc/generic/simple-card.c       | 2 +-
 sound/soc/generic/simple-scu-card.c   | 2 +-
 4 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index 7006150..afca6bd 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -36,6 +36,7 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
 				      struct snd_soc_dai_link *dai_link,
 				      const char *fmt, ...);
 int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
+				     struct device_node *node,
 				     char *prefix);
 
 #define asoc_simple_card_parse_clk_cpu(node, dai_link, simple_dai)		\
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 85120f5..604f1f9 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -81,6 +81,7 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
 EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
 
 int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
+				     struct device_node *node,
 				     char *prefix)
 {
 	char prop[128];
@@ -89,7 +90,7 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
 	snprintf(prop, sizeof(prop), "%sname", prefix);
 
 	/* Parse the card name from DT */
-	ret = snd_soc_of_parse_card_name(card, prop);
+	ret = snd_soc_of_parse_card_name_from_node(card, node, prop);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index f608f8d2..342ff53 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -401,7 +401,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 			goto card_parse_end;
 	}
 
-	ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+	ret = asoc_simple_card_parse_card_name(&priv->snd_card, NULL, PREFIX);
 	if (ret < 0)
 		goto card_parse_end;
 
diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c
index 348e9a7..a8164a2 100644
--- a/sound/soc/generic/simple-scu-card.c
+++ b/sound/soc/generic/simple-scu-card.c
@@ -239,7 +239,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 		i++;
 	}
 
-	ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+	ret = asoc_simple_card_parse_card_name(&priv->snd_card, NULL, PREFIX);
 	if (ret < 0)
 		return ret;
 
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 07/12] ASoC: simple-card-utils: add asoc_simple_card_try_to_probe_graph_card()
From: Kuninori Morimoto @ 2016-11-16  2:20 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

If CPU/Platform side driver probes successfully, and if it is supporting
both previous normal sound card style and graph style DT, it can call
asoc_simple_card_try_to_probe_graph_card().
It checks graph style DT, and do nothing if it was non graph style DT,
or register new simple-graph-card driver if graph style DT.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 include/sound/simple_card_utils.h     |  8 +++++++
 sound/soc/generic/simple-card-utils.c | 45 +++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index 4b58954..7006150 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -22,6 +22,10 @@ struct asoc_simple_dai {
 	struct clk *clk;
 };
 
+struct asoc_simple_graph_card_info {
+	int endpoint_num; /* sound endpoint number */
+};
+
 int asoc_simple_card_parse_daifmt(struct device *dev,
 				  struct device_node *node,
 				  struct device_node *codec,
@@ -78,4 +82,8 @@ void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
 
 int asoc_simple_card_clean_reference(struct snd_soc_card *card);
 
+void asoc_simple_card_try_to_probe_graph_card(struct device *dev,
+				struct asoc_simple_graph_card_info *info);
+
+
 #endif /* __SIMPLE_CARD_CORE_H */
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 373ada3..85120f5 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
@@ -285,6 +286,50 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card)
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
 
+void asoc_simple_card_try_to_probe_graph_card(struct device *dev,
+				struct asoc_simple_graph_card_info *info)
+{
+	struct platform_device_info pdevinfo;
+	struct device_node *node;
+	const char *compatible;
+	int ret;
+
+	node = of_graph_get_top_port(dev);
+	if (!node || !info)
+		/*
+		 * It doesn't have graph base sound DT, or its infomation.
+		 * Do nothing here, It assumes that system has
+		 * normal sound card.
+		 */
+		return;
+
+	ret = of_property_read_string(node, "compatible", &compatible);
+	if (ret < 0)
+		goto probe_err;
+
+	/*
+	 * FIXME
+	 *
+	 * It should use of_platform_xxx() instead of
+	 * platform_device_register_full() ? but there is no solution.
+	 * see also
+	 * linux/sound/soc/generic/simple-graph-card.c :: asoc_simple_card_probe
+	 */
+
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent		= dev;
+	pdevinfo.id		= PLATFORM_DEVID_AUTO;
+	pdevinfo.name		= compatible;
+	pdevinfo.dma_mask	= DMA_BIT_MASK(32);
+	pdevinfo.data		= info;
+	pdevinfo.size_data	= sizeof(*info);
+	platform_device_register_full(&pdevinfo);
+
+probe_err:
+	of_node_put(node);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_try_to_probe_graph_card);
+
 /* Module information */
 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
 MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 06/12] ASoC: simple-card-utils: add asoc_simple_card_parse_graph_dai()
From: Kuninori Morimoto @ 2016-11-16  2:20 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

simple-card already has asoc_simple_card_parse_dai(),
but graph base parsing needs graph specific version of it.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 include/sound/simple_card_utils.h     | 10 +++++++
 sound/soc/generic/simple-card-utils.c | 50 +++++++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index fd641255..4b58954 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -59,6 +59,16 @@ int asoc_simple_card_parse_dai(struct device_node *node,
 				  const char *cells_name,
 				  int *is_single_links);
 
+#define asoc_simple_card_parse_graph_cpu(ep, dai_link)			\
+	asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node,	\
+					 &dai_link->cpu_dai_name)
+#define asoc_simple_card_parse_graph_codec(ep, dai_link)		\
+	asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node,	\
+					 &dai_link->codec_dai_name)
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
+				     struct device_node **endpoint_np,
+				     const char **dai_name);
+
 int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
 			      struct asoc_simple_dai *simple_dai);
 
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index e5b80f5..373ada3 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
 #include <sound/simple_card_utils.h>
 
 int asoc_simple_card_parse_daifmt(struct device *dev,
@@ -164,6 +165,55 @@ int asoc_simple_card_parse_dai(struct device_node *node,
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
 
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
+				     struct device_node **dai_of_node,
+				     const char **dai_name)
+{
+	struct device_node *node, *port, *endpoint;
+	int i, id;
+
+	if (!ep)
+		return 0;
+
+	/*
+	 * of_graph_get_port_parent() will call
+	 * of_node_put(). So, call of_node_get() here
+	 */
+	of_node_get(ep);
+	node = of_graph_get_port_parent(ep);
+
+	i = 0;
+	id = -1;
+	for_each_of_port(node, port) {
+		for_each_of_endpoint_in_port(port, endpoint) {
+			if (endpoint == ep)
+				id = i;
+			i++;
+		}
+	}
+	if (id < 0)
+		return -ENODEV;
+
+	/* Get dai->name */
+	if (dai_name) {
+		struct of_phandle_args args;
+		int ret;
+
+		args.np		= node;
+		args.args[0]	= id;
+		args.args_count	= (i > 1);
+
+		ret = snd_soc_get_dai_name(&args, dai_name);
+		if (ret < 0)
+			return ret;
+	}
+
+	*dai_of_node = node;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
+
 int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
 			      struct asoc_simple_dai *simple_dai)
 {
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 05/12] of_graph: add of_graph_get_port/endpoint_count()
From: Kuninori Morimoto @ 2016-11-16  2:19 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>


From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

OF graph want to count its port/endpoint number, same as
of_get_child_count(). This patch adds these functions.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 drivers/of/base.c        | 24 ++++++++++++++++++++++++
 include/linux/of_graph.h |  8 ++++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index b11f533..e795d0f 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2584,3 +2584,27 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
 	return of_get_next_parent(np);
 }
 EXPORT_SYMBOL(of_graph_get_remote_port);
+
+int of_graph_get_port_count(const struct device_node *np)
+{
+	struct device_node *port;
+	int num = 0;
+
+	for_each_of_port(np, port)
+		num++;
+
+	return num;
+}
+EXPORT_SYMBOL(of_graph_get_port_count);
+
+int of_graph_get_endpoint_count(const struct device_node *np)
+{
+	struct device_node *port, *endpoint;
+	int num = 0;
+
+	for_each_of_endpoint(np, port, endpoint)
+		num++;
+
+	return num;
+}
+EXPORT_SYMBOL(of_graph_get_endpoint_count);
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index 8207631..9089459 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -54,6 +54,8 @@ struct of_endpoint {
 #ifdef CONFIG_OF
 int of_graph_parse_endpoint(const struct device_node *node,
 				struct of_endpoint *endpoint);
+int of_graph_get_port_count(const struct device_node *np);
+int of_graph_get_endpoint_count(const struct device_node *np);
 struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
 struct device_node *of_graph_get_top_port(struct device *dev);
 struct device_node *of_graph_get_next_port(const struct device_node *parent,
@@ -79,6 +81,12 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
 	return -ENOSYS;
 }
 
+static inline int of_graph_get_endpoint_count(const struct device_node *np,
+					      char *type)
+{
+	return 0;
+}
+
 static inline struct device_node *of_graph_get_port_by_id(
 					struct device_node *node, u32 id)
 {
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 04/12] of_graph: add for_each_of_port() / for_each_of_endpoint_in_port()
From: Kuninori Morimoto @ 2016-11-16  2:19 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Linux-ALSA, Liam Girdwood, Simon, Laurent, Guennadi, Grant Likely,
	Frank Rowand, Linux-DT, Linux-Kernel
In-Reply-To: <87polww4o2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

From: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>

OF graph is used mainly from V4L2, but ALSA needs to use it. It already
has for_each_endpoint_of_node() which is for-loop for each endpoint.
But, ALSA needs for-loop for each port[s], and for-loop for each
endpoint of inside port[s]. This patch adds for_each_of_port()
and for_each_of_endpoint_in_port() for this purpose.

And it also adds for_each_of_endpoint() which is similar to
for_each_endpoint_of_node(). The difference is it can catch port
handle during for-loop.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v3 -> v4

 - no change

 drivers/of/base.c        | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_graph.h | 29 ++++++++++++++++++++++
 2 files changed, 93 insertions(+)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index e49eb28..b11f533 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2354,6 +2354,70 @@ struct device_node *of_graph_get_top_port(struct device *dev)
 EXPORT_SYMBOL(of_graph_get_top_port);
 
 /**
+ * of_graph_get_next_port() - get next port node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+					   struct device_node *prev)
+{
+	struct device_node *port;
+	struct device_node *node;
+
+	if (!parent)
+		return NULL;
+
+	node = of_get_child_by_name(parent, "ports");
+	if (node)
+		parent = node;
+
+	/*
+	 * Start by locating the port node. If no previous endpoint is specified
+	 * search for the first port node, otherwise get the previous endpoint
+	 * parent port node.
+	 */
+	if (!prev) {
+		port = of_get_child_by_name(parent, "port");
+		if (!port)
+			pr_err("%s(): no port node found in %s\n",
+			       __func__, parent->full_name);
+	} else {
+		do {
+			port = of_get_next_child(parent, prev);
+			if (!port)
+				break;
+		} while (of_node_cmp(port->name, "port"));
+	}
+
+	of_node_put(node);
+
+	return port;
+}
+EXPORT_SYMBOL(of_graph_get_next_port);
+
+/**
+ * of_graph_get_next_endpoint_in_port() - get next endpoint node in port
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_endpoint_in_port(
+			const struct device_node *port,
+			struct device_node *prev)
+{
+	if (!port)
+		return NULL;
+
+	return of_get_next_child(port, prev);
+}
+EXPORT_SYMBOL(of_graph_get_next_endpoint_in_port);
+
+/**
  * of_graph_get_next_endpoint() - get next endpoint node
  * @parent: pointer to the parent device node
  * @prev: previous endpoint node, or NULL to get first
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index 23b1c6e..8207631 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -30,6 +30,16 @@ struct of_endpoint {
 	const struct device_node *local_node;
 };
 
+#define for_each_of_port(parent, port) \
+	for (port = of_graph_get_next_port(parent, NULL); port != NULL; \
+	     port = of_graph_get_next_port(parent, port))
+#define for_each_of_endpoint_in_port(port, ep) \
+	for (ep = of_graph_get_next_endpoint_in_port(port, NULL); ep != NULL; \
+	     ep = of_graph_get_next_endpoint_in_port(port, ep))
+#define for_each_of_endpoint(parent, port, ep) \
+	for_each_of_port(parent, port) \
+		for_each_of_endpoint_in_port(port, ep)
+
 /**
  * for_each_endpoint_of_node - iterate over every endpoint in a device node
  * @parent: parent device node containing ports and endpoints
@@ -46,6 +56,11 @@ int of_graph_parse_endpoint(const struct device_node *node,
 				struct of_endpoint *endpoint);
 struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
 struct device_node *of_graph_get_top_port(struct device *dev);
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+					struct device_node *prev);
+struct device_node *of_graph_get_next_endpoint_in_port(
+	const struct device_node *port,
+	struct device_node *prev);
 struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
 					struct device_node *previous);
 struct device_node *of_graph_get_endpoint_by_regs(
@@ -75,6 +90,20 @@ static inline struct device_node *of_graph_get_top_port(struct device *dev)
 	return NULL;
 }
 
+static inline struct device_node *of_graph_get_next_port(
+					const struct device_node *parent,
+					struct device_node *prev)
+{
+	return NULL;
+}
+
+static inline struct device_node *of_graph_get_next_endpoint_in_port(
+					const struct device_node *port,
+					struct device_node *prev)
+{
+	return NULL;
+}
+
 static inline struct device_node *of_graph_get_next_endpoint(
 					const struct device_node *parent,
 					struct device_node *previous)
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related


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